1 /* 2 * $RCSfile: CompileState.java,v $ 3 * 4 * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. 5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6 * 7 * This code is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 only, as 9 * published by the Free Software Foundation. Sun designates this 10 * particular file as subject to the "Classpath" exception as provided 11 * by Sun in the LICENSE file that accompanied this code. 12 * 13 * This code is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 * version 2 for more details (a copy is included in the LICENSE file that 17 * accompanied this code). 18 * 19 * You should have received a copy of the GNU General Public License version 20 * 2 along with this work; if not, write to the Free Software Foundation, 21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 22 * 23 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 24 * CA 95054 USA or visit www.sun.com if you need additional information or 25 * have any questions. 26 * 27 * $Revision: 1.6 $ 28 * $Date: 2008/02/28 20:17:20 $ 29 * $State: Exp $ 30 */ 31 32 package javax.media.j3d; 33 34 import java.util.*; 35 import java.security.*; 36 37 /** 38 * The CompileState holds information used during a compile. It is 39 * passed to each SceneGraphObject (SGO) during the compile. Each SGO 40 * modifies the CompileState as necessary and passes the CompileState 41 * to its children (if any). 42 * 43 * The CompileState currently has two functions: appearance mapping 44 * and shape merging. 45 * 46 * Appearance mapping maintains a list of the unique appearances seen 47 * during the compile. getAppearance() is used to turn multiple, 48 * equivalent and static appearances into a single shared appearance. 49 * 50 * The shape mergings collects shapes that are potentially mergable 51 * during a compile. The shapes are sorted into a Map of Lists of 52 * shapes, using the shape's appearance as the key. After a subtree 53 * is traversed, the shapes are merged and added to the Group. 54 */ 55 56 class CompileState { 57 // Appearance mapping stuff: 58 HashMap knownAppearances = new HashMap(); 59 int numAppearances = 0; 60 int numShared = 0; 61 int numShapes = 0; 62 63 // Shape merging stuff: 64 HashMap shapeLists = null; // top entry in shapeListStack 65 int numMergeSets = 0; 66 int numMergeShapes = 0; 67 boolean compileVerbose = false; 68 69 70 static final int BOUNDS_READ = 0x00001; 71 static final int GEOMETRY_READ = 0x00002; 72 73 74 // scene graph flattening 75 76 boolean keepTG = false; // used to force the immediate transform 77 // group to stay around 78 79 boolean needNormalsTransform = false; // true if the current transform 80 // needs to push down normals 81 // transform to geometries 82 83 // the current static transform group 84 TransformGroupRetained staticTransform = null; 85 86 // parent group 87 GroupRetained parentGroup = null; 88 89 // list of transform group 90 // for the current transform group 91 ArrayList transformGroupChildrenList = null; 92 93 // list of objects that have a static 94 // transform that can be deferenced 95 // after compile 96 ArrayList staticTransformObjects = new ArrayList(1); 97 98 int numTransformGroups = 0; 99 int numStaticTransformGroups = 0; 100 int numMergedTransformGroups = 0; 101 int numGroups = 0; 102 int numMergedGroups = 0; 103 int numShapesWSharedGeom = 0; 104 int numShapesWStaticTG = 0; 105 int numLinks = 0; 106 int numSwitches = 0; 107 int numOrderedGroups = 0; 108 int numMorphs = 0; 109 CompileState()110 CompileState() { 111 try { 112 compileVerbose = Boolean.getBoolean("javax.media.j3d.compileVerbose"); 113 } catch (AccessControlException e) { 114 compileVerbose = false; 115 } 116 initShapeMerge(); 117 } 118 119 // Appearance mapping: 120 /** 121 * Returns an unique appearance which equals app. If appearance does not 122 * equal any previously found, the appearance will be added to the known 123 * appearances and be returned. If the apperance equals a previously known 124 * appearance, then the prevously known apperance will be returned 125 */ getAppearance(AppearanceRetained app)126 AppearanceRetained getAppearance(AppearanceRetained app) { 127 AppearanceRetained retval; 128 129 // see if the appearance has allready been classified 130 if (app.map == this) { 131 if (app.mapAppearance != null) { 132 numShared++; 133 return app.mapAppearance; 134 } 135 } 136 137 // check if this appearance equals one one in the Map 138 if ((retval = (AppearanceRetained)knownAppearances.get(app)) != null) { 139 numShared++; 140 } else { 141 // not found, put this appearance in the map 142 knownAppearances.put(app, app); 143 numAppearances++; 144 numShared++; // sharing with self... 145 retval = app; 146 } 147 148 // cache this result on the appearance in case it appears again 149 app.map = this; 150 app.mapAppearance = retval; 151 152 return retval; 153 } 154 155 // Shape Merging: initShapeMerge()156 private void initShapeMerge() { 157 shapeLists = new HashMap(); 158 159 } 160 addShape(Shape3DRetained shape)161 void addShape(Shape3DRetained shape) { 162 if (parentGroup != null) { 163 // sort the shapes into lists with equivalent appearances 164 Vector list; 165 if ((list = (Vector)shapeLists.get(shape.appearance)) == null) { 166 list = new Vector(); 167 shapeLists.put(shape.appearance, list); 168 } 169 // Add the shape to the list only if at its parent level 170 // no children can be added or removed .. 171 // Look for the first non-null geometry, there should be atleast 172 // one otherwise it wouldn't come here 173 GeometryRetained geometry = null; 174 int i = 0; 175 while (geometry == null && i < shape.geometryList.size()) { 176 geometry = (GeometryRetained) shape.geometryList.get(i); 177 i++; 178 } 179 if (shape.parent instanceof GroupRetained && ((GroupRetained)shape.parent).isStaticChildren() && geometry.geoType < GeometryArrayRetained.GEO_TYPE_RASTER) { 180 list.add(shape); 181 } 182 183 } 184 } 185 186 printStats()187 void printStats() { 188 System.err.println("numTransformGroups= " + numTransformGroups); 189 System.err.println("numStaticTransformGroups= " + numStaticTransformGroups); 190 System.err.println("numMergedTransformGroups= " + numMergedTransformGroups); 191 System.err.println("numGroups= " + numGroups); 192 System.err.println("numMergedGroups= " + numMergedGroups); 193 System.err.println("numShapes= " + numShapes); 194 System.err.println("numShapesWStaticTG= " + numShapesWStaticTG); 195 System.err.println("numMergeShapes= " + numMergeShapes); 196 System.err.println("numMergeSets= " + numMergeSets); 197 System.err.println("numLinks= " + numLinks); 198 System.err.println("numSwitches= " + numSwitches); 199 System.err.println("numOrderedGroups= " + numOrderedGroups); 200 System.err.println("numMorphs= " + numMorphs); 201 } 202 doShapeMerge()203 void doShapeMerge() { 204 205 // System.err.println("doShapeMerge, shapeList = "+shapeLists); 206 if (shapeLists != null) { 207 // loop over the shapes in each list, creating a single shape 208 // for each. Add the shape to the group 209 Collection lists = shapeLists.values(); 210 Iterator listIterator = lists.iterator(); 211 Shape3DRetained mergeShape; 212 GeometryRetained firstGeo; 213 int num = 0; 214 int compileFlags = 0; 215 216 while (listIterator.hasNext()) { 217 Vector curList = (Vector)listIterator.next(); 218 int numShapes = curList.size(); 219 Shape3DRetained[] shapes = new Shape3DRetained[numShapes]; 220 curList.copyInto(shapes); 221 Shape3DRetained[] toBeMergedShapes = new Shape3DRetained[numShapes]; 222 for (int i = 0; i < numShapes; i++) { 223 if (shapes[i] == null) { 224 continue; 225 } 226 firstGeo = null; 227 num = 0; 228 // Get the first non-null geometry 229 while (firstGeo == null && num < shapes[i].geometryList.size()) { 230 firstGeo = (GeometryRetained) shapes[i].geometryList.get(num); 231 num++; 232 } 233 234 if (firstGeo != null && firstGeo instanceof GeometryArrayRetained) { 235 int numMerge = 0; 236 mergeShape = shapes[i]; 237 GeometryArrayRetained mergeGeo = (GeometryArrayRetained)firstGeo; 238 239 toBeMergedShapes[numMerge++] = mergeShape; 240 // Determine if all mergeable shapes have the same boundsCompute 241 // and collisionBounds set the same way 242 compileFlags = getCompileFlags(mergeShape); 243 for (int j = i+1; j < numShapes; j++) { 244 if (shapes[j] == null) { 245 continue; 246 } 247 firstGeo = null; 248 num = 0; 249 // Get the first non-null geometry 250 while (firstGeo == null && num < shapes[j].geometryList.size()) { 251 firstGeo = (GeometryRetained) shapes[j].geometryList.get(num); 252 num++; 253 } 254 255 // There is a non-null geometry for this shape .. 256 if (firstGeo != null && 257 shapes[j].isEquivalent(mergeShape) && 258 firstGeo.isEquivalenceClass(mergeGeo) && 259 ((GeometryArrayRetained)firstGeo).vertexFormat == mergeGeo.vertexFormat) { 260 // got one to merge, add shapes to merge, 261 toBeMergedShapes[numMerge++] = shapes[j]; 262 263 compileFlags |= getCompileFlags(shapes[j]); 264 265 // remove from shapes 266 shapes[j] = null; 267 } 268 } 269 if (numMerge > 1) { 270 271 // remove the shapes from its parent before merge 272 // They all should 273 GroupRetained group = (GroupRetained)toBeMergedShapes[0].parent; 274 Shape3DRetained s; 275 for (int n = 0; n < numMerge; n++) { 276 s = toBeMergedShapes[n]; 277 boolean found = false; 278 int numChilds = group.numChildren(); 279 for (int k = 0; (k < numChilds && !found); k++) { 280 if (group.getChild(k).retained == s) { 281 found = true; 282 group.removeChild(k); 283 } 284 } 285 if (!found) { 286 System.err.println("ShapeSet.add(): Can't remove " + 287 "shape from parent, can't find shape!"); 288 } 289 290 } 291 292 mergeShape = new Shape3DCompileRetained(toBeMergedShapes, numMerge, compileFlags); 293 294 if (J3dDebug.devPhase && J3dDebug.debug) { 295 if (J3dDebug.doDebug(J3dDebug.compileState, J3dDebug.LEVEL_3)) { 296 System.err.println("Dest is "+ parentGroup); 297 System.err.println("Compile Shape "+mergeShape); 298 System.err.println(mergeShape.geometryList.size()+" geoemtryList"); 299 for (int j = 0; j < mergeShape.geometryList.size(); j++) { 300 GeometryRetained geo = ((GeometryRetained)mergeShape.geometryList.get(j)); 301 if (geo != null) 302 System.err.println("\t Geo_type = "+geo.geoType); 303 } 304 305 System.err.println(numMerge+" Shapes were merged "); 306 for (int j = 0; j < numMerge; j++) { 307 System.err.println("\t" + toBeMergedShapes[j]); 308 } 309 } 310 } 311 312 // Set the source to one of the merged shape's source 313 mergeShape.setSource(toBeMergedShapes[0].source); 314 numMergeSets++; 315 numMergeShapes += numMerge ; 316 parentGroup.addChild((Node)mergeShape.source); 317 } 318 } 319 // add the shape to the dest 320 } 321 } 322 } 323 324 // Clear the shapelists for the next merge 325 shapeLists.clear(); 326 327 } 328 329 getCompileFlags(Shape3DRetained shape)330 int getCompileFlags(Shape3DRetained shape) { 331 int cflag = 0; 332 333 // If allow intersect is turned on , then geometry is readable 334 if (shape.allowIntersect() || 335 shape.source.getCapability(Shape3D.ALLOW_GEOMETRY_READ)|| 336 (shape.boundsAutoCompute && 337 shape.source.getCapability(Shape3D.ALLOW_BOUNDS_READ))) 338 cflag |= GEOMETRY_READ; 339 340 return cflag; 341 342 } 343 344 } 345