001/** 002 * J3DTriangleList -- A Java3D node that represents a TriangleList in a J3D scene graph. 003 * 004 * Copyright (C) 2015-2016, Joseph A. Huwaldt. All rights reserved. 005 * 006 * This library is free software; you can redistribute it and/or modify it under the terms 007 * of the GNU Lesser General Public License as published by the Free Software Foundation; 008 * either version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, but WITHOUT ANY 011 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 012 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 013 * 014 * You should have received a copy of the GNU Lesser General Public License along with 015 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - 016 * Suite 330, Boston, MA 02111-1307, USA. Or visit: http://www.gnu.org/licenses/lgpl.html 017 */ 018package geomss.j3d; 019 020import geomss.app.GeomSSCanvas3D; 021import geomss.geom.GeomTriangle; 022import geomss.geom.TriangleList; 023import static java.util.Objects.requireNonNull; 024import javax.media.j3d.*; 025import javax.vecmath.*; 026import javolution.context.StackContext; 027 028/** 029 * A Java 3D node that represents a TriangleList in a Java 3D scene graph. 030 * 031 * <p> Modified by: Joseph A. Huwaldt </p> 032 * 033 * @author Joseph A. Huwaldt, Date: April 13, 2009 034 * @version September 9, 2016 035 */ 036public class J3DTriangleList extends J3DGeomGroup<TriangleList> { 037 038 // The switch for main or mirrored geometry. 039 private Switch _symmSG; 040 041 // Store the Shape3D so it could potentially be used again in the future. 042 private TriangleListShape3D _oldSolidShape; 043 private TriangleListShape3D _oldWireframeShape; 044 private Shape3D _oldPointShape; 045 046 /** 047 * Construct a J3DTriangleList using the specified TriangleList as a reference. 048 * 049 * @param canvas The canvas that the geometry is being rendered into. 050 * @param geometry The GeomSS geometry to be turned into a Java3D node. 051 */ 052 public J3DTriangleList(GeomSSCanvas3D canvas, TriangleList geometry) { 053 super(requireNonNull(canvas), requireNonNull(geometry)); 054 } 055 056 /** 057 * Set the display of a mirrored copy of this geometry. This is called from 058 * "setDisplayed()" to turn on and off the display of mirrored geometry without 059 * changing the "mirrored state" of the object. This call does not affect the output 060 * of "isMirrored()". 061 * 062 * @param mirrored Flag indicating if the mirrored geometry should be displayed or 063 * not. 064 */ 065 @Override 066 protected void internalSetMirrored(boolean mirrored) { 067 if (mirrored) 068 _symmSG.setWhichChild(Switch.CHILD_ALL); 069 else 070 _symmSG.setWhichChild(0); 071 } 072 073 /** 074 * Create a new Java 3D <code>Group</code> that contains the geometry contained in 075 * this object. This method is called from <code>createSceneGraph</code>. Sub-classes 076 * must over-ride this method to provide geometry specific implementations. 077 * 078 * @return New Group that contains the geometry in this object. 079 * @see #createSceneGraph 080 */ 081 @Override 082 @SuppressWarnings("null") 083 protected Group createGeometry() { 084 J3DRenderingPrefs drawPrefs = getRenderingPrefs(); 085 TriangleList<? extends GeomTriangle> triLst = this.getGeomElement(); 086 TriangleListShape3D solid = null; 087 TriangleListShape3D wireFrame = null; 088 Shape3D pointShape = null; 089 090 // Try to recycle a previous rendering of this geometry element if at all possible. 091 J3DTriangleList oldGroup = (J3DTriangleList)getOldJ3DGeomGroup(); 092 if (oldGroup != null) { 093 J3DRenderingPrefs oldPrefs = oldGroup.getRenderingPrefs(); 094 if (oldPrefs.getDrawTolerance().equals(drawPrefs.getDrawTolerance())) { 095 //System.out.println("Re-using solidShape & wireframeShape"); 096 // Draw tolerance is unchanged, so re-use the existing Shape3D. 097 solid = (TriangleListShape3D)oldGroup._oldSolidShape.cloneNode(true); 098 wireFrame = (TriangleListShape3D)oldGroup._oldWireframeShape.cloneNode(true); 099 pointShape = (Shape3D)oldGroup._oldPointShape.cloneNode(true); 100 101 // Has the point color changed? 102 if (!oldPrefs.getPointColor().equals(drawPrefs.getPointColor())) { 103 //System.out.println("Changing point color"); 104 Color4f pointColor = drawPrefs.getPointColorJ3D(); 105 PointArray pointA = (PointArray)pointShape.getGeometry(); 106 int size = pointA.getVertexCount(); 107 for (int i = 0; i < size; ++i) { 108 pointA.setColor(i, pointColor); 109 } 110 } 111 112 } 113 } 114 115 // If we couldn't recycle the solid & wireFrame shapes, then create new ones. 116 if (solid == null) { 117 StackContext.enter(); 118 try { 119 solid = new TriangleListShape3D(triLst, triLst); 120 } finally { 121 StackContext.exit(); 122 } 123 124 // Wireframe is the same geometry, just differnet display attributes. 125 wireFrame = (TriangleListShape3D)solid.cloneNode(true); 126 127 // Create a new PointArray geometry for the point shape. 128 GeometryArray geom = (GeometryArray)solid.getGeometry(); 129 int numPnts = geom.getValidVertexCount(); 130 PointArray pointA = new PointArray(numPnts, PointArray.COORDINATES | PointArray.COLOR_4); 131 Color4f pointColor = drawPrefs.getPointColorJ3D(); 132 Point3f aPnt = new Point3f(); 133 for (int i = 0; i < numPnts; ++i) { 134 geom.getCoordinate(i, aPnt); 135 pointA.setCoordinate(i, aPnt); 136 pointA.setColor(i, pointColor); 137 } 138 pointShape = new GeomShape3D(triLst, pointA); 139 140 } 141 this._oldSolidShape = (TriangleListShape3D)solid.cloneNode(true); // Save off for potential re-use in the future. 142 this._oldWireframeShape = (TriangleListShape3D)wireFrame.cloneNode(true); 143 this._oldPointShape = (Shape3D)pointShape.cloneNode(true); 144 145 // Define wireframe attributes. 146 PolygonAttributes wireFrameAttrib = new PolygonAttributes(); 147 wireFrameAttrib.setPolygonMode(PolygonAttributes.POLYGON_LINE); 148 wireFrameAttrib.setCullFace(PolygonAttributes.CULL_NONE); 149 wireFrameAttrib.setPolygonOffset(-2); // Make wireframe appear "on top" of solid version. 150 151 // Define wireframe material. 152 Material wireMaterial = new Material(); 153 Color4f color = drawPrefs.getLineColorJ3D(); 154 wireMaterial.setDiffuseColor(color.x, color.y, color.z); 155 wireMaterial.setLightingEnable(true); 156 157 // Create a default solid version. 158 Appearance srfAppearance = drawPrefs.getSurfaceAppearance(); 159 PolygonAttributes polyAttrib = new PolygonAttributes(); 160 polyAttrib.setCullFace(PolygonAttributes.CULL_NONE); 161 polyAttrib.setBackFaceNormalFlip(true); // Flip surface normals for back faces. 162 srfAppearance.setPolygonAttributes(polyAttrib); 163 LineAttributes lineAttrib = new LineAttributes(); 164 lineAttrib.setLineWidth(drawPrefs.getLineWidth()); 165 srfAppearance.setLineAttributes(lineAttrib); 166 solid.setAppearance(srfAppearance); 167 168 // Create a duplicate wireframe version. 169 Appearance wfApp = (Appearance)srfAppearance.cloneNodeComponent(false); 170 wfApp.setPolygonAttributes(wireFrameAttrib); 171 wfApp.setMaterial(wireMaterial); 172 wireFrame.setAppearance(wfApp); 173 174 // Create a sold + wireframe version. 175 BranchGroup solidWireFrame = new BranchGroup(); 176 solidWireFrame.addChild(solid.cloneTree()); 177 solidWireFrame.addChild(wireFrame.cloneTree()); 178 179 // Create a switch group for the main geometry (unmirrored) 180 // that contains each variation on the display properties of an array. 181 Switch renderTypeSG = new Switch(0); 182 renderTypeSG.setCapability(Switch.ALLOW_SWITCH_READ); 183 renderTypeSG.setCapability(Switch.ALLOW_SWITCH_WRITE); 184 185 // Create a 3D shape from the point array 186 Appearance appearance = new Appearance(); 187 pointShape.setAppearance(appearance); 188 appearance.setPointAttributes(new PointAttributes(drawPrefs.getPointSize(), true)); 189 190 // Add the geometry to the main geometry switch. 191 renderTypeSG.addChild(solidWireFrame); 192 renderTypeSG.addChild(solid); 193 renderTypeSG.addChild(wireFrame); 194 renderTypeSG.addChild(pointShape); 195 196 // Add the basic unmirrored geometry to the switch. 197 _symmSG = new Switch(); 198 _symmSG.setCapability(Switch.ALLOW_SWITCH_READ); 199 _symmSG.setCapability(Switch.ALLOW_SWITCH_WRITE); 200 _symmSG.addChild(renderTypeSG); 201 202 // Clone the basic geometry to make the mirrored geometry. 203 Switch mirrored = new Switch(0); 204 mirrored.setCapability(Switch.ALLOW_SWITCH_READ); 205 mirrored.setCapability(Switch.ALLOW_SWITCH_WRITE); 206 207 // Clone the basic geometry and reverse normals to make the mirrored geometry. 208 TriangleListShape3D solidM = reverseNormals((TriangleListShape3D)solid.cloneTree(true)); 209 BranchGroup solidWireFrameM = new BranchGroup(); 210 solidWireFrameM.addChild(solidM.cloneTree()); 211 solidWireFrameM.addChild(wireFrame.cloneTree()); 212 213 // Add the geometry to the mirrored geometry switch. 214 mirrored.addChild(solidWireFrameM); 215 mirrored.addChild(solidM); 216 mirrored.addChild(wireFrame.cloneTree()); 217 mirrored.addChild(pointShape.cloneTree()); 218 219 // Create a mirror across the XZ plane of symmetry transform. 220 Transform3D symmT = new Transform3D(); 221 symmT.setScale(new Vector3d(1, -1, 1)); 222 TransformGroup symmTG = new TransformGroup(symmT); 223 224 // Add the mirrored geometry to the symmetry transform group. 225 symmTG.addChild(mirrored); 226 227 // Add the mirrored geometry to the switch. 228 _symmSG.addChild(symmTG); 229 230 // By default, display only the main geometry (not the mirrored). 231 _symmSG.setWhichChild(0); 232 233 return _symmSG; 234 } 235 236 /** 237 * Reverse the surface normals in the specified TriangleListShape3D object. 238 */ 239 private static TriangleListShape3D reverseNormals(TriangleListShape3D shape) { 240 241 // Reverse the surface normals in the geometry for the mirrored shape. 242 GeometryArray array = (GeometryArray)shape.getGeometry(); 243 int numVerts = array.getVertexCount(); 244 Vector3f normal = new Vector3f(); 245 for (int i = 0; i < numVerts; ++i) { 246 array.getNormal(i, normal); 247 normal.negate(); 248 array.setNormal(i, normal); 249 } 250 251 return shape; 252 } 253 254 /** 255 * Set the render type used for this group. 256 * 257 * @param type Value indicating the way that PointArray objects should be rendered. 258 */ 259 @Override 260 public void setRenderType(RenderType type) { 261 super.setRenderType(requireNonNull(type)); 262 263 // Get the main geometry's render switch. 264 Switch mainRenderSG = (Switch)_symmSG.getChild(0); 265 266 // Get the mirrored geometry's render switch. 267 Switch mirrRenderSG = (Switch)((Group)_symmSG.getChild(1)).getChild(0); 268 269 // Set switches based on the render type code. 270 switch (type) { 271 case SOLID_PLUS_WIREFRAME: 272 mainRenderSG.setWhichChild(0); 273 mirrRenderSG.setWhichChild(0); 274 break; 275 276 case SOLID: 277 mainRenderSG.setWhichChild(1); 278 mirrRenderSG.setWhichChild(1); 279 break; 280 281 case WIREFRAME: 282 case STRINGS: 283 mainRenderSG.setWhichChild(2); 284 mirrRenderSG.setWhichChild(2); 285 break; 286 287 case POINTS: 288 mainRenderSG.setWhichChild(3); 289 mirrRenderSG.setWhichChild(3); 290 break; 291 292 default: 293 break; 294 295 } 296 297 // Hide symmetric array geometry when POINTS or STRINGS are shown. 298 internalSetMirrored(isMirrored()); 299 } 300 301 /** 302 * Creates a new instance of the node. This routine is called by 303 * <code>cloneTree</code> to duplicate the current node. 304 * 305 * @param forceDuplicate when set to <code>true</code>, causes the 306 * <code>duplicateOnCloneTree</code> flag to be ignored. When 307 * <code>false</code>, the value of each node's 308 * <code>duplicateOnCloneTree</code> variable determines whether 309 * NodeComponent data is duplicated or copied. 310 * @return A new instance of this Java3D node. 311 */ 312 @Override 313 public Node cloneNode(boolean forceDuplicate) { 314 J3DTriangleList node = new J3DTriangleList(this.getCanvas3D(), this.getGeomElement()); 315 node.duplicateNode(this, forceDuplicate); 316 return node; 317 } 318}