001: /*
002: * $RCSfile: BehaviorRetained.java,v $
003: *
004: * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
006: *
007: * This code is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU General Public License version 2 only, as
009: * published by the Free Software Foundation. Sun designates this
010: * particular file as subject to the "Classpath" exception as provided
011: * by Sun in the LICENSE file that accompanied this code.
012: *
013: * This code is distributed in the hope that it will be useful, but WITHOUT
014: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: * version 2 for more details (a copy is included in the LICENSE file that
017: * accompanied this code).
018: *
019: * You should have received a copy of the GNU General Public License version
020: * 2 along with this work; if not, write to the Free Software Foundation,
021: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
022: *
023: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
024: * CA 95054 USA or visit www.sun.com if you need additional information or
025: * have any questions.
026: *
027: * $Revision: 1.6 $
028: * $Date: 2008/02/28 20:17:19 $
029: * $State: Exp $
030: */
031:
032: package javax.media.j3d;
033:
034: import java.util.ArrayList;
035:
036: /**
037: * Behavior is an abstract class that contains the framework for all
038: * behavioral components in Java 3D.
039: */
040:
041: class BehaviorRetained extends LeafRetained {
042: // These bitmasks are used to quickly tell what conditions this behavior
043: // is waiting for. Currently BehaviorStructure only used 4 of them.
044: static final int WAKEUP_ACTIVATE_INDEX = 0;
045: static final int WAKEUP_DEACTIVATE_INDEX = 1;
046: static final int WAKEUP_VP_ENTRY_INDEX = 2;
047: static final int WAKEUP_VP_EXIT_INDEX = 3;
048: static final int WAKEUP_TIME_INDEX = 4;
049:
050: static final int NUM_WAKEUPS = 5;
051:
052: static final int WAKEUP_ACTIVATE = 0x0001;
053: static final int WAKEUP_DEACTIVATE = 0x0002;
054: static final int WAKEUP_VP_ENTRY = 0x0004;
055: static final int WAKEUP_VP_EXIT = 0x0008;
056: static final int WAKEUP_TIME = 0x0010;
057:
058: /**
059: * The number of scheduling intervals supported by this
060: * implementation. This is fixed for a particular implementation
061: * and must be at least 10.
062: */
063: static final int NUM_SCHEDULING_INTERVALS = 10;
064:
065: // different types of IndexedUnorderedSet that use in BehaviorStructure
066: static final int BEHAIVORS_IN_BS_LIST = 0;
067: static final int SCHEDULE_IN_BS_LIST = 1;
068:
069: // total number of different IndexedUnorderedSet types
070: static final int TOTAL_INDEXED_UNORDER_SET_TYPES = 2;
071:
072: /**
073: * The Boundary object defining the behavior's scheduling region.
074: */
075: Bounds schedulingRegion = null;
076:
077: /**
078: * The bounding leaf reference
079: */
080: BoundingLeafRetained boundingLeaf = null;
081:
082: /**
083: * The current wakeup condition.
084: */
085: WakeupCondition wakeupCondition = null;
086:
087: /**
088: * This is the new WakeupCondition to be set in
089: * initialize wakeupOn()
090: */
091: WakeupCondition newWakeupCondition = null;
092:
093: /**
094: * The current view platform for this behavior; this value is
095: * false until it comes into range of a view platform.
096: */
097: ViewPlatformRetained vp = null;
098:
099: /**
100: * The current activation status for this behavior; this value
101: * is false until it comes into range of a view platform.
102: */
103: boolean active = false;
104:
105: /**
106: * Flag indicating whether the behavior is enabled.
107: */
108: boolean enable = true;
109:
110: /**
111: * Current scheduling interval.
112: */
113: int schedulingInterval = NUM_SCHEDULING_INTERVALS / 2;
114:
115: /**
116: * This is a flag that tells the behavior scheduler whether the
117: * user-programmed process stimulus called wakeupOn, if it did
118: * not, then the wakeupCondition will be set to null.
119: */
120: boolean conditionSet = false;
121:
122: /**
123: * This is a flag that indicates whether we are in an initialize or
124: * processStimulus callback. If wakeupOn is called for this behavior
125: * when this flag is not set, an exception will be thrown.
126: */
127: boolean inCallback = false;
128:
129: /**
130: * This is a flag that indicates whether we are in initialize
131: * callback. If wakeupOn is called for this behavior when
132: * this flag is true, then its
133: * buildTree() will delay until insert nodes message
134: * is get. This is because some localToVworld[] that wakeup
135: * depends may not initialize when this behavior setLive().
136: */
137: boolean inInitCallback = false;
138:
139: /**
140: * The transformed schedulingRegion
141: */
142: Bounds transformedRegion = null;
143:
144: // A bitmask that indicates that the scheduling region has changed.
145: int isDirty = 0xffff;
146:
147: /**
148: * A bitmask that represents all conditions that this behavior is waiting on.
149: */
150: int wakeupMask = 0;
151:
152: /**
153: * An array of ints that count how many of each wakup is present
154: */
155: int[] wakeupArray = new int[NUM_WAKEUPS];
156:
157: // use to post message when bounds change, always point to this
158: Object targets[] = new Object[1];
159:
160: BehaviorRetained() {
161: this .nodeType = NodeRetained.BEHAVIOR;
162: localBounds = new BoundingBox();
163: ((BoundingBox) localBounds).setLower(1.0, 1.0, 1.0);
164: ((BoundingBox) localBounds).setUpper(-1.0, -1.0, -1.0);
165: targets[0] = this ;
166: IndexedUnorderSet.init(this , TOTAL_INDEXED_UNORDER_SET_TYPES);
167: }
168:
169: /**
170: * Get the Behavior's scheduling region.
171: * @return this Behavior's scheduling region information
172: */
173: Bounds getSchedulingBounds() {
174: Bounds b = null;
175:
176: if (schedulingRegion != null) {
177: b = (Bounds) schedulingRegion.clone();
178: if (staticTransform != null) {
179: Transform3D invTransform = staticTransform
180: .getInvTransform();
181: b.transform(invTransform);
182: }
183: }
184: return b;
185: }
186:
187: /**
188: * Set the Behavior's scheduling region.
189: * @param region a region that contains the Behavior's new scheduling
190: * bounds
191: */
192: synchronized void setSchedulingBounds(Bounds region) {
193:
194: if (region != null) {
195: schedulingRegion = (Bounds) region.clone();
196: if (staticTransform != null) {
197: schedulingRegion.transform(staticTransform.transform);
198: }
199: } else {
200: schedulingRegion = null;
201: }
202:
203: if (source != null && source.isLive()) {
204: sendMessage(J3dMessage.REGION_BOUND_CHANGED);
205: }
206: }
207:
208: /**
209: * Set the Sound's scheduling region to the specified Leaf node.
210: */
211: synchronized void setSchedulingBoundingLeaf(BoundingLeaf region) {
212:
213: if (source != null && source.isLive()) {
214: if (boundingLeaf != null)
215: boundingLeaf.mirrorBoundingLeaf.removeUser(this );
216: }
217:
218: if (region != null) {
219: boundingLeaf = (BoundingLeafRetained) region.retained;
220: } else {
221: boundingLeaf = null;
222: }
223:
224: if (source != null && source.isLive()) {
225: if (boundingLeaf != null)
226: boundingLeaf.mirrorBoundingLeaf.addUser(this );
227: sendMessage(J3dMessage.REGION_BOUND_CHANGED);
228: }
229: }
230:
231: /**
232: * Enables or disables this Behavior. The default state is enabled.
233: * @param state true or false to enable or disable this Behavior
234: */
235: void setEnable(boolean state) {
236: if (enable != state) {
237: enable = state;
238: if (source != null && source.isLive()) {
239: sendMessage(state ? J3dMessage.BEHAVIOR_ENABLE
240: : J3dMessage.BEHAVIOR_DISABLE);
241: }
242: }
243: }
244:
245: /**
246: * Retrieves the state of the Behavior enable flag.
247: * @return the Behavior enable state
248: */
249: boolean getEnable() {
250: return enable;
251: }
252:
253: /**
254: * Sets the scheduling interval of this Behavior node to the
255: * specified value.
256: * @param schedulingInterval the new scheduling interval
257: */
258: void setSchedulingInterval(int schedulingInterval) {
259:
260: if ((source != null) && source.isLive() && !inCallback) {
261: // avoid MT safe problem when user thread setting
262: // this while behavior scheduling using this.
263: sendMessage(J3dMessage.SCHEDULING_INTERVAL_CHANGED,
264: new Integer(schedulingInterval));
265: } else {
266: // garantee this setting reflect in next frame
267: this .schedulingInterval = schedulingInterval;
268: }
269: }
270:
271: /**
272: * Retrieves the current scheduling interval of this Behavior
273: * node.
274: *
275: * @return the current scheduling interval
276: */
277: int getSchedulingInterval() {
278: return schedulingInterval;
279: }
280:
281: /**
282: * Get the Behavior's scheduling region
283: */
284: BoundingLeaf getSchedulingBoundingLeaf() {
285: return (boundingLeaf != null ? (BoundingLeaf) boundingLeaf.source
286: : null);
287: }
288:
289: /**
290: * This setLive routine first calls the superclass's method, then
291: * it activates all canvases that are associated with the attached
292: * view.
293: */
294: synchronized void setLive(SetLiveState s) {
295:
296: super .doSetLive(s);
297: if (inBackgroundGroup) {
298: throw new IllegalSceneGraphException(J3dI18N
299: .getString("BehaviorRetained0"));
300: }
301: if (inSharedGroup) {
302: throw new IllegalSharingException(J3dI18N
303: .getString("BehaviorRetained1"));
304: }
305:
306: s.nodeList.add(this );
307: s.behaviorNodes.add(this );
308: s.notifyThreads |= J3dThread.UPDATE_BEHAVIOR;
309: // process switch leaf
310: if (s.switchTargets != null && s.switchTargets[0] != null) {
311: s.switchTargets[0].addNode(this , Targets.BEH_TARGETS);
312: }
313: switchState = (SwitchState) s.switchStates.get(0);
314:
315: if (boundingLeaf != null) {
316: boundingLeaf.mirrorBoundingLeaf.addUser(this );
317: }
318: if (s.transformTargets != null && s.transformTargets[0] != null) {
319: s.transformTargets[0].addNode(this , Targets.BEH_TARGETS);
320: s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
321: }
322: super .markAsLive();
323: }
324:
325: /**
326: * This clearLive routine first calls the superclass's method, then
327: * it deactivates all canvases that are associated with the attached
328: * view.
329: */
330: synchronized void clearLive(SetLiveState s) {
331: super .clearLive(s);
332: s.nodeList.add(this );
333: if (s.transformTargets != null && s.transformTargets[0] != null) {
334: s.transformTargets[0].addNode(this , Targets.BEH_TARGETS);
335: s.notifyThreads |= J3dThread.UPDATE_TRANSFORM;
336: }
337: s.notifyThreads |= J3dThread.UPDATE_BEHAVIOR;
338: if (s.switchTargets != null && s.switchTargets[0] != null) {
339: s.switchTargets[0].addNode(this , Targets.BEH_TARGETS);
340: }
341: if (boundingLeaf != null) {
342: boundingLeaf.mirrorBoundingLeaf.removeUser(this );
343: }
344: // BehaviorStructure removeBehavior() will do the
345: // wakeupCondition.cleanTree() over there.
346: }
347:
348: /**
349: * This routine execute the user's initialize method
350: */
351: void executeInitialize() {
352:
353: synchronized (this ) {
354: boolean inCallbackSaved = inCallback;
355: boolean inInitCallbackSaved = inInitCallback;
356:
357: inCallback = true;
358: inInitCallback = true;
359: try {
360: ((Behavior) this .source).initialize();
361: } catch (RuntimeException e) {
362: System.err
363: .println("Exception occurred during Behavior initialization:");
364: e.printStackTrace();
365: } catch (Error e) {
366: // Issue 264 - catch Error
367: System.err
368: .println("Error occurred during Behavior initialization:");
369: e.printStackTrace();
370: }
371: inCallback = inCallbackSaved;
372: inInitCallback = inInitCallbackSaved;
373: }
374: }
375:
376: /**
377: * Defines this behavior's wakeup criteria.
378: * @param criteria The wakeup criterion for this object
379: */
380: void wakeupOn(WakeupCondition criteria) {
381: // If not call by initialize(), buildTree will
382: // delay until insertNodes in BehaviorStructure
383: // Otherwise BehaviorScheduler will invoke
384: // handleLastWakeupOn()
385: if (criteria == null) {
386: throw new NullPointerException(J3dI18N
387: .getString("BehaviorRetained2"));
388: }
389:
390: if (!inInitCallback) {
391: conditionSet = true;
392: wakeupCondition = criteria;
393: } else {
394: // delay setting wakeup condition in BehaviorStructure
395: // activateBehaviors(). This is because there may have
396: // previously wakeupCondition attach to it and
397: // scheduling even after clearLive() due to message
398: // delay processing. It is not MT safe to set it
399: // in user thread.
400: newWakeupCondition = criteria;
401: }
402:
403: }
404:
405: // The above wakeupOn() just remember the reference
406: // We only need to handle (and ignore the rest) the
407: // last wakeupOn() condition set in the behavior.
408: // This handle the case when multiple wakeupOn()
409: // are invoked in the same processStimulus()
410: void handleLastWakeupOn(WakeupCondition prevWakeupCond,
411: BehaviorStructure bs) {
412:
413: if (bs == universe.behaviorStructure) {
414: if (wakeupCondition == prevWakeupCond) {
415: // reuse the same wakeupCondition
416: wakeupCondition.resetTree();
417: } else {
418: if (prevWakeupCond != null) {
419: prevWakeupCond.cleanTree(bs);
420: }
421: wakeupCondition.buildTree(null, 0, this );
422: }
423: } else {
424: // No need to do prevWakeupCond.cleanTree(bs)
425: // since removeBehavior() will do so
426: }
427: }
428:
429: /**
430: * Returns this behavior's wakeup criteria.
431: * @return criteria The wakeup criteria of this object
432: */
433: WakeupCondition getWakeupCondition() {
434: return wakeupCondition;
435: }
436:
437: /**
438: * Post the specified Id. Behaviors use this method to cause sequential
439: * scheduling of other behavior object.
440: * @param postId The Id being posted
441: */
442:
443: void postId(int postId) {
444: if (source != null && source.isLive()) {
445: universe.behaviorStructure.handleBehaviorPost(
446: (Behavior) source, postId);
447: }
448: }
449:
450: protected View getView() {
451: return (universe != null ? universe.getCurrentView() : null);
452: }
453:
454: synchronized void updateTransformRegion(Bounds bound) {
455: if (boundingLeaf == null) {
456: updateTransformRegion();
457: } else {
458: if (bound == null) {
459: transformedRegion = null;
460: } else {
461: transformedRegion = (Bounds) bound.clone();
462: transformedRegion
463: .transform(boundingLeaf.mirrorBoundingLeaf
464: .getCurrentLocalToVworld());
465: }
466: }
467: }
468:
469: synchronized void updateTransformRegion() {
470: if (boundingLeaf == null
471: || !boundingLeaf.mirrorBoundingLeaf.switchState.currentSwitchOn) {
472: if (schedulingRegion == null) {
473: transformedRegion = null;
474: } else {
475: // use schedulingRegion
476: if (transformedRegion != null) {
477: transformedRegion.set(schedulingRegion);
478: } else {
479: transformedRegion = (Bounds) schedulingRegion
480: .clone();
481: }
482: transformedRegion.transform(getCurrentLocalToVworld());
483:
484: }
485: } else {
486: // use boundingLeaf
487: transformedRegion = boundingLeaf.mirrorBoundingLeaf.transformedRegion;
488:
489: }
490: }
491:
492: // Note: This routine will only to update the object's
493: // transformed region
494: void updateBoundingLeaf(long refTime) {
495: transformedRegion = (Bounds) boundingLeaf.mirrorBoundingLeaf.transformedRegion;
496: }
497:
498: void addWakeupCondition() {
499: }
500:
501: final void sendMessage(int mtype, Object arg) {
502: J3dMessage createMessage = new J3dMessage();
503: createMessage.threads = J3dThread.UPDATE_BEHAVIOR;
504: createMessage.type = mtype;
505: createMessage.universe = universe;
506: createMessage.args[0] = targets;
507: createMessage.args[1] = this ;
508: createMessage.args[2] = arg;
509: VirtualUniverse.mc.processMessage(createMessage);
510: }
511:
512: final void sendMessage(int mtype) {
513: sendMessage(mtype, null);
514: }
515:
516: void mergeTransform(TransformGroupRetained xform) {
517: super .mergeTransform(xform);
518: if (schedulingRegion != null) {
519: schedulingRegion.transform(xform.transform);
520: }
521: if (source instanceof DistanceLOD) {
522: ((DistanceLOD) source).mergeTransform(xform);
523: }
524: }
525: }
|