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