001: /*
002: * $RCSfile: TransparencyOrderedGroup.java,v $
003: *
004: * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * - Redistribution of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * - Redistribution in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * Neither the name of Sun Microsystems, Inc. or the names of
019: * contributors may be used to endorse or promote products derived
020: * from this software without specific prior written permission.
021: *
022: * This software is provided "AS IS," without a warranty of any
023: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
024: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
025: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
026: * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
027: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
028: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
029: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
030: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
031: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
032: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
033: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
034: * POSSIBILITY OF SUCH DAMAGES.
035: *
036: * You acknowledge that this software is not designed, licensed or
037: * intended for use in the design, construction, operation or
038: * maintenance of any nuclear facility.
039: *
040: * $Revision: 1.2 $
041: * $Date: 2007/02/09 17:17:02 $
042: * $State: Exp $
043: */
044:
045: package org.jdesktop.j3d.utils.scenegraph.transparency;
046:
047: import java.util.Arrays;
048: import javax.media.j3d.Group;
049: import javax.media.j3d.Node;
050: import javax.media.j3d.Shape3D;
051: import javax.media.j3d.Transform3D;
052: import javax.media.j3d.TransformGroup;
053: import org.jdesktop.j3d.utils.scenegraph.traverser.ProcessNodeInterface;
054: import org.jdesktop.j3d.utils.scenegraph.traverser.TreeScan;
055:
056: /**
057: * Provides the user control over the rendering order of transparent objects
058: * in the subgraphs of this Group.
059: * <br>
060: * Transparent shapes in the 1st child of this group will be rendered before
061: * (ie behind) transparent shapes in the 2nd child and so on.
062: *<br>
063: * Transparent shapes in the same child will be ordered using the standard test
064: * of distance to view.
065: *<br>
066: * TransparencyOrderedGroups can not be included under a SharedGroup. If they
067: * are a RuntimeException will be thrown when the graph is made live.
068: *<br>
069: * Even though this class is a subclass of TransformGroup it should be treated
070: * as a Group. The subclassing TransformGroup is an implementation detail,
071: * all of the TransformGroup specific methods will throw RuntimeExceptions.
072: *
073: * @author paulby
074: */
075: public class TransparencyOrderedGroup extends
076: javax.media.j3d.TransformGroup {
077:
078: private int[] childIndexOrder = null;
079: private boolean checkArr[] = null;
080: private final static Transform3D t3d = new Transform3D();
081:
082: /** Creates a new instance of TransparencyOrderedGroup */
083: public TransparencyOrderedGroup() {
084: super (new Transform3D());
085: setCapability(Group.ALLOW_CHILDREN_READ);
086: setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
087: }
088:
089: /**
090: * Sets the childIndexOrder array. If the specified array is
091: * null, this node's childIndexOrder array is set to null. Its
092: * children will then be rendered in increasing index order. If
093: * the specified array is not null, the entire array is copied to
094: * this node's childIndexOrder array. In this case, the length of
095: * the array must be equal to the number of children in this
096: * group, and every entry in the array must have a unique value
097: * between 0 and <code>numChildren-1</code> (i.e., there must be
098: * no duplicate values and no missing indices).
099: *
100: * @param childIndexOrder the array that is copied into this
101: * node's child index order array; this can be null
102: *
103: * @exception IllegalArgumentException if the specified array is
104: * non-null and any of the following are true:
105: * <ul>
106: * <li><code>childIndexOrder.length != numChildren</code>;</li>
107: * <li><code>childIndexOrder[</code><i>i</i><code>] < 0</code>,
108: * for <i>i</i> in <code>[0, numChildren-1]</code>;</li>
109: * <li><code>childIndexOrder[</code><i>i</i><code>] >= numChildren</code>,
110: * for <i>i</i> in <code>[0, numChildren-1]</code>;</li>
111: * <li><code>childIndexOrder[</code><i>i</i><code>] ==
112: * childIndexOrder[</code><i>j</i><code>]</code>,
113: * for <i>i</i>,<i>j</i> in <code>[0, numChildren-1]</code>,
114: * <i>i</i> <code>!=</code> <i>j</i>;</li>
115: * </ul>
116: *
117: */
118: public void setChildIndexOrder(int[] childIndexOrder) {
119: verifyChildIndexOrderArray(childIndexOrder, 0);
120: if (childIndexOrder == null) {
121: this .childIndexOrder = null;
122: return;
123: }
124:
125: if (this .childIndexOrder == null
126: || childIndexOrder.length != this .childIndexOrder.length)
127: this .childIndexOrder = new int[childIndexOrder.length];
128:
129: System.arraycopy(childIndexOrder, 0, this .childIndexOrder, 0,
130: childIndexOrder.length);
131: super .setTransform(t3d);
132: }
133:
134: /**
135: * Retrieves the current childIndexOrder array.
136: *
137: * @return a copy of this node's childIndexOrder array; this
138: * can be null.
139: *
140: */
141: public int[] getChildIndexOrder() {
142: if (childIndexOrder == null)
143: return null;
144:
145: int[] ret = new int[childIndexOrder.length];
146:
147: System.arraycopy(childIndexOrder, 0, ret, 0, ret.length);
148:
149: return ret;
150:
151: }
152:
153: /**
154: * Given the index of the child return the rendering order for that child
155: */
156: int getRenderingOrderForChild(int childIndex) {
157: if (childIndexOrder == null)
158: return childIndex;
159:
160: int ret = -1;
161: for (int i = 0; i < childIndexOrder.length && ret == -1; i++) {
162: if (childIndexOrder[i] == childIndex)
163: ret = i;
164: }
165:
166: if (ret == -1)
167: throw new RuntimeException("Child " + childIndex
168: + " not found in childIndexOrder array");
169:
170: return ret;
171: }
172:
173: /**
174: * Appends the specified child node to this group node's list of children.
175: *
176: * <p>
177: * If the current child index order array is non-null, the array
178: * is increased in size by one element, and a new element
179: * containing the index of the new child is added to the end of
180: * the array. Thus, this new child will be rendered last.
181: *
182: * @param child the child to add to this node's list of children
183: * @exception CapabilityNotSetException if the appropriate capability is
184: * not set and this group node is part of live or compiled scene graph
185: * @exception RestrictedAccessException if this group node is part
186: * of live
187: * or compiled scene graph and the child node being added is not
188: * a BranchGroup node
189: * @exception MultipleParentException if <code>child</code> has already
190: * been added as a child of another group node.
191: */
192: public void addChild(Node child) {
193: super .addChild(child);
194:
195: if (childIndexOrder != null) {
196: int[] tmp = new int[childIndexOrder.length + 1];
197: System.arraycopy(childIndexOrder, 0, tmp, 0,
198: childIndexOrder.length);
199: tmp[tmp.length - 1] = tmp.length - 1;
200: childIndexOrder = tmp;
201: }
202: }
203:
204: /**
205: * Appends the specified child node to this group node's list of
206: * children, and sets the child index order array to the specified
207: * array. If the specified array is null, this node's
208: * childIndexOrder array is set to null. Its children will then
209: * be rendered in increasing index order. If the specified array
210: * is not null, the entire array is copied to this node's
211: * childIndexOrder array. In this case, the length of the array
212: * must be equal to the number of children in this group after the
213: * new child has been added, and every entry in the array must
214: * have a unique value between 0 and <code>numChildren-1</code>
215: * (i.e., there must be no duplicate values and no missing
216: * indices).
217: *
218: * @param child the child to add to this node's list of children
219: *
220: * @param childIndexOrder the array that is copied into this
221: * node's child index order array; this can be null
222: *
223: * @exception CapabilityNotSetException if the appropriate capability is
224: * not set and this group node is part of live or compiled scene graph
225: *
226: * @exception RestrictedAccessException if this group node is part
227: * of live
228: * or compiled scene graph and the child node being added is not
229: * a BranchGroup node
230: *
231: * @exception MultipleParentException if <code>child</code> has already
232: * been added as a child of another group node.
233: *
234: * @exception IllegalArgumentException if the specified array is
235: * non-null and any of the following are true:
236: * <ul>
237: * <li><code>childIndexOrder.length != numChildren</code>;</li>
238: * <li><code>childIndexOrder[</code><i>i</i><code>] < 0</code>,
239: * for <i>i</i> in <code>[0, numChildren-1]</code>;</li>
240: * <li><code>childIndexOrder[</code><i>i</i><code>] >= numChildren</code>,
241: * for <i>i</i> in <code>[0, numChildren-1]</code>;</li>
242: * <li><code>childIndexOrder[</code><i>i</i><code>] ==
243: * childIndexOrder[</code><i>j</i><code>]</code>,
244: * for <i>i</i>,<i>j</i> in <code>[0, numChildren-1]</code>,
245: * <i>i</i> <code>!=</code> <i>j</i>;</li>
246: * </ul>
247: */
248: public void addChild(Node child, int[] childIndexOrder) {
249:
250: verifyChildIndexOrderArray(childIndexOrder, 1);
251:
252: this .childIndexOrder = new int[childIndexOrder.length];
253: System.arraycopy(childIndexOrder, 0, this .childIndexOrder, 0,
254: childIndexOrder.length);
255:
256: super .addChild(child);
257: }
258:
259: /**
260: * Inserts the specified child node in this group node's list of
261: * children at the specified index.
262: * This method is only supported when the child index order array
263: * is null.
264: *
265: * @param child the new child
266: * @param index at which location to insert. The <code>index</code>
267: * must be a value
268: * greater than or equal to 0 and less than or equal to
269: * <code>numChildren()</code>.
270: * @exception CapabilityNotSetException if the appropriate capability is
271: * not set and this group node is part of live or compiled scene graph
272: * @exception RestrictedAccessException if this group node is part of
273: * live
274: * or compiled scene graph and the child node being inserted is not
275: * a BranchGroup node
276: * @exception MultipleParentException if <code>child</code> has already
277: * been added as a child of another group node.
278: * @exception IndexOutOfBoundsException if <code>index</code> is invalid.
279: * @exception IllegalStateException if the childIndexOrder array is
280: * not null.
281: *
282: */
283: public void insertChild(Node child, int index) {
284: if (childIndexOrder != null) {
285: throw new IllegalStateException(
286: "insertChild illegal when childIndexOrder != null");
287: }
288: super .insertChild(child, index);
289: }
290:
291: /**
292: * Replaces the node at the specified index with the child node provided.
293: * This method is only supported when the child index order array
294: * is null.
295: *
296: * @param child the new child
297: * @param index of node to replace. The <code>index</code>
298: * must be a value
299: * greater than or equal to 0 and less than or equal to
300: * <code>numChildren()</code>.
301: * @exception CapabilityNotSetException if the appropriate capability is
302: * not set and this group node is part of live or compiled scene graph
303: * @exception RestrictedAccessException if this group node is part of
304: * live
305: * or compiled scene graph and the child node being inserted is not
306: * a BranchGroup node
307: * @exception MultipleParentException if <code>child</code> has already
308: * been added as a child of another group node.
309: * @exception IndexOutOfBoundsException if <code>index</code> is invalid.
310: * @exception IllegalStateException if the childIndexOrder array is
311: * not null.
312: *
313: */
314: public void setChild(Node child, int index) {
315: if (childIndexOrder != null) {
316: throw new IllegalStateException(
317: "setChild illegal when childIndexOrder != null");
318: }
319: super .setChild(child, index);
320: }
321:
322: /**
323: * Removes the specified child node from this group node's
324: * list of children.
325: * If the specified object is not in the list, the list is not modified.
326: *
327: * <p>
328: * If the current child index order array is non-null, the element
329: * containing the removed child's index will be removed from the
330: * child index order array, and the array will be reduced in size
331: * by one element. If the child removed was not the last child in
332: * the Group, the values of the child index order array will be
333: * updated to reflect the indices that were renumbered. More
334: * formally, each child whose index in the Group node was greater
335: * than the removed element (before removal) will have its index
336: * decremented by one.
337: *
338: * @param child the child node to be removed.
339: * @exception CapabilityNotSetException if appropriate capability is
340: * not set and this object is part of live or compiled scene graph
341: * @exception RestrictedAccessException if this group node is part of
342: * live or compiled scene graph and the child node being removed is not
343: * a BranchGroup node
344: */
345: public void removeChild(Node child) {
346: int index = indexOfChild(child);
347: super .removeChild(child);
348: removeChildFromIndexOrder(index);
349: }
350:
351: /**
352: * Removes the child node at the specified index from this group node's
353: * list of children.
354: *
355: * <p>
356: * If the current child index order array is non-null, the element
357: * containing the removed child's index will be removed from the
358: * child index order array, and the array will be reduced in size
359: * by one element. If the child removed was not the last child in
360: * the Group, the values of the child index order array will be
361: * updated to reflect the indices that were renumbered. More
362: * formally, each child whose index in the Group node was greater
363: * than the removed element (before removal) will have its index
364: * decremented by one.
365: *
366: * @param index which child to remove. The <code>index</code>
367: * must be a value
368: * greater than or equal to 0 and less than <code>numChildren()</code>.
369: * @exception CapabilityNotSetException if the appropriate capability is
370: * not set and this group node is part of live or compiled scene graph
371: * @exception RestrictedAccessException if this group node is part of
372: * live or compiled scene graph and the child node being removed is not
373: * a BranchGroup node
374: * @exception IndexOutOfBoundsException if <code>index</code> is invalid.
375: */
376: public void removeChild(int index) {
377: super .removeChild(index);
378: removeChildFromIndexOrder(index);
379: }
380:
381: /**
382: * TransparencyOrderedGroup should not be used as a TransformGroup, this
383: * method will always throw a RuntimeException
384: */
385: public void setTransform(Transform3D t) {
386: throw new RuntimeException(
387: "TransparencyOrderedGroup.setTransform should not be used.");
388: }
389:
390: /**
391: * TransparencyOrderedGroup should not be used as a TransformGroup, this
392: * method will always throw a RuntimeException
393: */
394: public void getTransform(Transform3D t) {
395: throw new RuntimeException(
396: "TransparencyOrderedGroup.getTransform should not be used.");
397: }
398:
399: private void removeChildFromIndexOrder(int index) {
400: if (childIndexOrder != null) {
401: int[] tmp = new int[childIndexOrder.length - 1];
402:
403: int newi = 0;
404: for (int i = 0; i < childIndexOrder.length; i++) {
405: int cio = childIndexOrder[i];
406:
407: if (cio > index) {
408: tmp[newi++] = cio - 1;
409: //System.out.println("----"+(cio-1)+" "+getChild(cio-1).getName());
410: if (isLive())
411: updateShapeChildIndicies(getChild(cio - 1), -1);
412: } else if (cio == index) {
413: // Remove this index
414: } else
415: tmp[newi++] = cio;
416: }
417:
418: childIndexOrder = tmp;
419: }
420: }
421:
422: /**
423: * Scan the graph from root applying the adjustment to the child index of
424: * each Shape3D for this tog
425: */
426: private void updateShapeChildIndicies(Node root,
427: final int indexAdjustment) {
428: final TransparencyOrderController controller = TransparencyOrderController
429: .getController();
430: TreeScan.findNode(root, Shape3D.class,
431: new ProcessNodeInterface() {
432: public boolean processNode(Node node) {
433: //System.out.println("Scan found "+node);
434: controller.adjustChildIndex((Shape3D) node,
435: indexAdjustment,
436: TransparencyOrderedGroup.this );
437: return true;
438: }
439: }, false, true);
440: }
441:
442: void verifyChildIndexOrderArray(int[] cIOArr, int plus) {
443:
444: if (cIOArr != null) {
445:
446: if (cIOArr.length != numChildren() + plus) {
447: throw new IllegalArgumentException(
448: "childIndexOrder.length != number of children");
449: }
450:
451: if ((checkArr == null)
452: || (checkArr.length != cIOArr.length)) {
453: checkArr = new boolean[cIOArr.length];
454: }
455:
456: Arrays.fill(checkArr, false);
457:
458: for (int i = 0; i < cIOArr.length; i++) {
459: if (cIOArr[i] < 0) {
460: throw new IllegalArgumentException(
461: "childIndexOrder[i] must be >= 0, for i in [0, numChildren-1]");
462: } else if (cIOArr[i] >= cIOArr.length) {
463: throw new IllegalArgumentException(
464: "childIndexOrder[i] must be < numChildren, for i in [0, numChildren-1]");
465: } else if (checkArr[cIOArr[i]]) {
466: throw new IllegalArgumentException(
467: "childIndexOrder[i] must not be equal to childIndexOrder[j], for i,j in [0,numChildren-1] and i != j");
468: } else {
469: checkArr[cIOArr[i]] = true;
470: }
471: }
472: }
473: }
474: }
|