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