/* * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. * */ package javax.media.j3d; import java.util.ArrayList; import java.util.Enumeration; import java.util.Vector; /** * The SharedGroup node provides the ability to share a scene graph from * multiple other scene graphs through the use of a Link node. */ class SharedGroupRetained extends GroupRetained implements TargetsInterface { /* static final int ILLEGAL_LEAF_MASK = 1 << NodeRetained.BACKGROUND | 1 << NodeRetained.BEHAVIOR | 1 << NodeRetained.CLIP | 1 << NodeRetained.LINEARFOG | 1 << NodeRetained.EXPONENTIALFOG | 1 << NodeRetained.SOUNDSCAPE | 1 << NodeRetained.VIEWPLATFORM | 1 << NodeRetained.BOUNDINGLEAF; */ // The current list of child transform group nodes or link nodes // under a transform group ArrayList childTransformLinks = new ArrayList(1); // key which identifies a unique path from a // locale to this transform group HashKey currentKey = new HashKey(); // key which identifies a unique path from a locale to this switch link HashKey switchKey = new HashKey(); /** * The Shared Group Node's parent vector. */ Vector parents = new Vector(1); // J3d copy. CachedTargets[] j3dCTs = null; // User copy. CachedTargets[] cachedTargets = null; // A bitmask of the types in targets for transform targets int localTargetThreads = 0; // combined localTargetThreads and decendants' localTargetThreads int targetThreads = 0; ArrayList switchStates = null; SharedGroupRetained() { this.nodeType = NodeRetained.SHAREDGROUP; } // SharedGroup specific data at SetLive. @Override void setAuxData(SetLiveState s, int index, int hkIndex) { int i, size; // Group's setAuxData() super.setAuxData(s, index, hkIndex); branchGroupPaths.add(hkIndex, s.branchGroupPaths.get(index)); if (orderedPaths == null) { orderedPaths = new ArrayList(1); } orderedPaths.add(hkIndex, s.orderedPaths.get(index)); if (switchStates == null) { switchStates = new ArrayList(1); } switchStates.add(hkIndex, s.switchStates.get(index)); if (viewLists == null) { viewLists = new ArrayList>(1); } // If there are some ViewSpecificGroups in the path above this SharedGroup // System.err.println("====> hkIndex = "+hkIndex+" s.viewLists = "+s.viewLists); if (s.viewLists != null) { viewLists.add(hkIndex, s.viewLists.get(index)); } else { viewLists.add(hkIndex, null); } if (lights == null) { lights = new ArrayList>(1); } if (s.lights != null) { lights.add(hkIndex, s.lights.get(index)); } else { lights.add(hkIndex, null); } if (fogs == null) { fogs = new ArrayList>(1); } if (s.fogs != null) { fogs.add(hkIndex, s.fogs.get(index)); } else { fogs.add(hkIndex, null); } if (modelClips == null) { modelClips = new ArrayList>(1); } if (s.modelClips != null) { modelClips.add(hkIndex, s.modelClips.get(index)); } else { modelClips.add(hkIndex, null); } if (altAppearances == null) { altAppearances = new ArrayList>(1); } if (s.altAppearances != null) { altAppearances.add(hkIndex, s.altAppearances.get(index)); } else { altAppearances.add(hkIndex, null); } } @Override void setNodeData(SetLiveState s) { // For inSharedGroup case. int i, j, len; if (localToVworld == null) { localToVworld = new Transform3D[s.keys.length][]; localToVworldIndex = new int[s.keys.length][]; localToVworldKeys = new HashKey[s.keys.length]; cachedTargets = new CachedTargets[s.keys.length]; len=0; } else { int newLen = localToVworld.length + s.keys.length; Transform3D newTList[][] = new Transform3D[newLen][]; HashKey newHList[] = new HashKey[newLen]; int newIndexList[][] = new int[newLen][]; CachedTargets newTargets[] = new CachedTargets[newLen]; len = localToVworld.length; // Copy the existing data into the newly created data objects. System.arraycopy(localToVworld, 0, newTList, 0, localToVworld.length); System.arraycopy(localToVworldIndex, 0, newIndexList, 0, localToVworldIndex.length); System.arraycopy(localToVworldKeys, 0, newHList, 0, localToVworldKeys.length); System.arraycopy(cachedTargets, 0, newTargets, 0, cachedTargets.length); localToVworld = newTList; localToVworldIndex = newIndexList; localToVworldKeys = newHList; cachedTargets = newTargets; } int[] hkIndex = new int[1]; int hkIndexPlus1, blkSize; s.hashkeyIndex = new int[s.keys.length]; // This should appear before super.setNodeData() if it exists s.parentBranchGroupPaths = branchGroupPaths; for(i=len, j=0; i(1); orderedPaths = null; switchStates = null; cachedTargets = null; targetThreads = 0; lights.clear(); fogs.clear(); modelClips.clear(); altAppearances.clear(); } else { int index, len; // Remove the localToVworld key int newLen = localToVworld.length - s.keys.length; Transform3D[][] newTList = new Transform3D[newLen][]; HashKey[] newHList = new HashKey[newLen]; Transform3D newChildTList[][] = null; int[][] newIndexList = new int[newLen][]; CachedTargets[] newTargets = new CachedTargets[newLen]; int[] tempIndex = new int[s.keys.length]; int curStart =0, newStart =0; boolean found = false; for(i=0;i= 0) { found = true; if(index == curStart) { curStart++; } else { len = index - curStart; System.arraycopy(localToVworld, curStart, newTList, newStart, len); System.arraycopy(localToVworldIndex, curStart, newIndexList, newStart, len); System.arraycopy(localToVworldKeys, curStart, newHList, newStart, len); System.arraycopy(cachedTargets, curStart, newTargets, newStart, len); curStart = index+1; newStart = newStart + len; } } else { found = false; MasterControl.getCoreLogger().severe("Can't Find matching hashKey in SG.removeNodeData."); } } if((found == true) && (curStart < localToVworld.length)) { len = localToVworld.length - curStart; System.arraycopy(localToVworld, curStart, newTList, newStart, len); System.arraycopy(localToVworldIndex, curStart, newIndexList, newStart, len); System.arraycopy(localToVworldKeys, curStart, newHList, newStart, len); System.arraycopy(cachedTargets, curStart, newTargets, newStart, len); } // Must be in reverse, to preserve right indexing. for (i = tempIndex.length-1; i >= 0 ; i--) { if(tempIndex[i] >= 0) { branchGroupPaths.remove(tempIndex[i]); orderedPaths.remove(tempIndex[i]); switchStates.remove(tempIndex[i]); lights.remove(tempIndex[i]); fogs.remove(tempIndex[i]); modelClips.remove(tempIndex[i]); altAppearances.remove(tempIndex[i]); } } localToVworld = newTList; localToVworldIndex = newIndexList; localToVworldKeys = newHList; cachedTargets = newTargets; } s.localToVworld = localToVworld; s.localToVworldIndex = localToVworldIndex; s.localToVworldKeys = localToVworldKeys; s.orderedPaths = orderedPaths; s.switchStates = switchStates; s.viewLists = viewLists; s.lights = lights; s.fogs = fogs; s.modelClips = modelClips; s.altAppearances = altAppearances; } @Override void clearLive(SetLiveState s) { int i,j,k, index; Transform3D savedLocalToVworld[][] = s.localToVworld; int savedLocalToVworldIndex[][] = s.localToVworldIndex; HashKey savedLocalToVworldKeys[] = s.localToVworldKeys; ArrayList savedOrderedPaths = s.orderedPaths; ArrayList> savedViewLists = s.viewLists; ArrayList> savedLights = s.lights; ArrayList> savedFogs = s.fogs; ArrayList> savedMclips = s.modelClips; ArrayList> savedAltApps = s.altAppearances; Targets[] savedSwitchTargets = s.switchTargets; Targets[] savedTransformTargets = s.transformTargets; // no need to gather targets from sg in clear live s.transformTargets = null; s.switchTargets = null; // XXXX: This is a hack since removeNodeData is called before // children are clearLives int[] tempIndex = null; // Don't keep the indices if everything will be cleared if (s.keys.length != localToVworld.length) { tempIndex = new int[s.keys.length]; for (i = s.keys.length-1; i >= 0; i--) { tempIndex[i] = s.keys[i].equals(localToVworldKeys, 0, localToVworldKeys.length); } } super.clearLive(s); // Do this after children clearlive since part of the viewLists may get cleared // during removeNodeData if(refCount <= 0) { viewLists.clear(); } else { // Must be in reverse, to preserve right indexing. for (i = tempIndex.length-1; i >= 0 ; i--) { if(tempIndex[i] >= 0) { viewLists.remove(tempIndex[i]); } } } // restore setLiveState from it's local variables. // removeNodeData has altered these variables. s.localToVworld = savedLocalToVworld; s.localToVworldIndex = savedLocalToVworldIndex; s.localToVworldKeys = savedLocalToVworldKeys; s.orderedPaths = savedOrderedPaths; s.viewLists = savedViewLists; s.lights = savedLights; s.fogs = savedFogs; s.modelClips = savedMclips; s.altAppearances = savedAltApps; s.transformTargets = savedTransformTargets; s.switchTargets = savedSwitchTargets; } void updateChildLocalToVworld(HashKey key, int index, ArrayList dirtyTransformGroups, ArrayList keySet, UpdateTargets targets, ArrayList blUsers) { LinkRetained ln; TransformGroupRetained tg; int i,j; Object obj; CachedTargets ct = j3dCTs[index]; if (ct != null) { targets.addCachedTargets(ct); if (ct.targetArr[Targets.BLN_TARGETS] != null) { gatherBlUsers(blUsers, ct.targetArr[Targets.BLN_TARGETS]); } } synchronized(childTransformLinks) { for (i=0; i updateList) { SwitchRetained sw; LinkRetained ln; Object obj; int i,j,k; ArrayList childSwitchLinks = childrenSwitchLinks.get(child); for (i=0; i>(1); } childrenSwitchLinks.add(index, new ArrayList(1)); } @Override void appendChildrenData() { if (childrenSwitchLinks == null) { childrenSwitchLinks = new ArrayList>(1); } childrenSwitchLinks.add(new ArrayList(1)); } @Override void removeChildrenData(int index) { ArrayList oldSwitchLinks = childrenSwitchLinks.get(index); oldSwitchLinks.clear(); childrenSwitchLinks.remove(index); } // *************************** // TargetsInterface methods // *************************** @Override public int getTargetThreads(int type) { if (type == TargetsInterface.TRANSFORM_TARGETS) { return targetThreads; } else { System.err.println("getTargetThreads: wrong arguments"); return -1; } } @Override TargetsInterface getClosestTargetsInterface(int type) { return this; } // re-evalute localTargetThreads using newCachedTargets and // re-evaluate targetThreads @Override public void computeTargetThreads(int type, CachedTargets[] newCachedTargets) { localTargetThreads = 0; if (type == TargetsInterface.TRANSFORM_TARGETS) { for(int i=0; i getTargetsData(int type, int index) { // index is ignores for SharedGroup if (type == TargetsInterface.SWITCH_TARGETS) { return switchStates; } else { System.err.println("getTargetsData: wrong arguments"); return null; } } @Override void childDoSetLive(NodeRetained child, int childIndex, SetLiveState s) { int i; s.childSwitchLinks = childrenSwitchLinks.get(childIndex); s.switchStates = switchStates; if(child!=null) child.setLive(s); } void childCheckSetLive(NodeRetained child, int childIndex, SetLiveState s) { s.childTransformLinks = childTransformLinks; s.parentTransformLink = this; child.setLive(s); } /** * Make the boundsCache of this node and all its parents dirty */ @Override void dirtyBoundsCache() { // Possible optimisation is to not traverse up the tree // if the cachedBounds==null. However this is not the case // if the node is the child of a SharedGroup if (VirtualUniverse.mc.cacheAutoComputedBounds) { // Issue 514 : NPE in Wonderland : triggered in cached bounds computation validCachedBounds = false; synchronized(parents) { Enumeration e = parents.elements(); while(e.hasMoreElements()) { LinkRetained parent = (LinkRetained) e.nextElement(); if (parent!=null) { parent.dirtyBoundsCache(); } } } } } }