summaryrefslogtreecommitdiffstats
path: root/src/classes
diff options
context:
space:
mode:
authorKevin Rushforth <[email protected]>2004-06-09 04:13:21 +0000
committerKevin Rushforth <[email protected]>2004-06-09 04:13:21 +0000
commit7fad83338964ecd32303d6e2737b19963492ecfe (patch)
tree886fd0b010966b11d6f25865715de4d5aa75cbb6 /src/classes
parent4fc115fa7dfc9fe2d614a5e7d3a0db49431930cf (diff)
Initial creation of j3d-core-utils sources in CVS repository
git-svn-id: https://svn.java.net/svn/j3d-core-utils~svn/trunk@5 9497e636-51bd-65ba-982d-a4982e1767a5
Diffstat (limited to 'src/classes')
-rw-r--r--src/classes/ToolsVersion9
-rw-r--r--src/classes/share/com/sun/j3d/ExceptionStrings.properties68
-rw-r--r--src/classes/share/com/sun/j3d/audioengines/AudioEngine.java208
-rw-r--r--src/classes/share/com/sun/j3d/audioengines/AudioEngine3D.java497
-rw-r--r--src/classes/share/com/sun/j3d/audioengines/AudioEngine3DL2.java308
-rw-r--r--src/classes/share/com/sun/j3d/audioengines/AudioEngineThread.java275
-rw-r--r--src/classes/share/com/sun/j3d/audioengines/AuralParameters.java197
-rw-r--r--src/classes/share/com/sun/j3d/audioengines/Sample.java479
-rwxr-xr-xsrc/classes/share/com/sun/j3d/audioengines/javasound/JSAuralParameters.java79
-rw-r--r--src/classes/share/com/sun/j3d/audioengines/javasound/JSChannel.java435
-rwxr-xr-xsrc/classes/share/com/sun/j3d/audioengines/javasound/JSClip.java349
-rwxr-xr-xsrc/classes/share/com/sun/j3d/audioengines/javasound/JSDirectionalSample.java738
-rwxr-xr-xsrc/classes/share/com/sun/j3d/audioengines/javasound/JSMidi.java67
-rwxr-xr-xsrc/classes/share/com/sun/j3d/audioengines/javasound/JSPositionalSample.java1339
-rwxr-xr-xsrc/classes/share/com/sun/j3d/audioengines/javasound/JSSample.java362
-rwxr-xr-xsrc/classes/share/com/sun/j3d/audioengines/javasound/JSStream.java67
-rwxr-xr-xsrc/classes/share/com/sun/j3d/audioengines/javasound/JSThread.java855
-rwxr-xr-xsrc/classes/share/com/sun/j3d/audioengines/javasound/JavaSoundMixer.java959
-rwxr-xr-xsrc/classes/share/com/sun/j3d/internal/BufferWrapper.java186
-rwxr-xr-xsrc/classes/share/com/sun/j3d/internal/ByteBufferWrapper.java197
-rwxr-xr-xsrc/classes/share/com/sun/j3d/internal/ByteOrderWrapper.java101
-rw-r--r--src/classes/share/com/sun/j3d/internal/Distance.java1096
-rwxr-xr-xsrc/classes/share/com/sun/j3d/internal/DoubleBufferWrapper.java153
-rw-r--r--src/classes/share/com/sun/j3d/internal/FastVector.java143
-rwxr-xr-xsrc/classes/share/com/sun/j3d/internal/FloatBufferWrapper.java152
-rw-r--r--src/classes/share/com/sun/j3d/internal/J3dUtilsI18N.java63
-rw-r--r--src/classes/share/com/sun/j3d/internal/UtilFreelistManager.java98
-rw-r--r--src/classes/share/com/sun/j3d/internal/UtilMemoryFreelist.java292
-rw-r--r--src/classes/share/com/sun/j3d/loaders/IncorrectFormatException.java62
-rw-r--r--src/classes/share/com/sun/j3d/loaders/Loader.java176
-rw-r--r--src/classes/share/com/sun/j3d/loaders/LoaderBase.java147
-rw-r--r--src/classes/share/com/sun/j3d/loaders/ParsingErrorException.java62
-rw-r--r--src/classes/share/com/sun/j3d/loaders/Scene.java148
-rw-r--r--src/classes/share/com/sun/j3d/loaders/SceneBase.java311
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/DebugOutput.java85
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/EnvelopeHandler.java133
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/FloatValueInterpolator.java171
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/ImageScaler.java148
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/J3dLwoParser.java601
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LWOBFileReader.java266
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LightIntensityPathInterpolator.java101
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/Lw3dLoader.java706
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwLightObject.java100
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwoParser.java424
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwoSurface.java316
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwoTexture.java284
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsBackground.java163
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsCamera.java179
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelope.java160
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelopeFrame.java105
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelopeLightIntensity.java153
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsFog.java143
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsFrame.java341
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsLight.java269
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsMotion.java688
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsObject.java570
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsPrimitive.java68
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/ParserObject.java91
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/SequenceLine.java269
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/SequenceReader.java172
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/ShapeHolder.java267
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/SwitchPathInterpolator.java129
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/TargaReader.java199
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/TextfileParser.java245
-rw-r--r--src/classes/share/com/sun/j3d/loaders/objectfile/DefaultMaterials.java952
-rw-r--r--src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFile.java1334
-rw-r--r--src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFileMaterials.java425
-rw-r--r--src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFileParser.java179
-rw-r--r--src/classes/share/com/sun/j3d/loaders/objectfile/RgbFile.java202
-rw-r--r--src/classes/share/com/sun/j3d/utils/applet/JMainFrame.java347
-rw-r--r--src/classes/share/com/sun/j3d/utils/applet/MainFrame.java397
-rw-r--r--src/classes/share/com/sun/j3d/utils/audio/DistanceAttenuation.java134
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/interpolators/CubicSplineCurve.java175
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/interpolators/CubicSplineSegment.java543
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/interpolators/KBCubicSplineCurve.java174
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/interpolators/KBCubicSplineSegment.java630
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/interpolators/KBKeyFrame.java150
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/interpolators/KBRotPosScaleSplinePathInterpolator.java313
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/interpolators/KBSplinePathInterpolator.java301
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/interpolators/RotPosScaleTCBSplinePathInterpolator.java247
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/interpolators/TCBKeyFrame.java144
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/interpolators/TCBSplinePathInterpolator.java278
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/keyboard/KeyNavigator.java499
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/keyboard/KeyNavigatorBehavior.java216
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/mouse/MouseBehavior.java329
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/mouse/MouseBehaviorCallback.java73
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/mouse/MouseRotate.java315
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/mouse/MouseTranslate.java290
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/mouse/MouseZoom.java271
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/picking/Intersect.java1297
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/picking/PickMouseBehavior.java152
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/picking/PickObject.java841
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/picking/PickRotateBehavior.java194
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/picking/PickTranslateBehavior.java178
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/picking/PickZoomBehavior.java180
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/picking/PickingCallback.java73
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/sensor/Mouse6DPointerBehavior.java195
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorBeamEcho.java231
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorButtonListener.java94
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorEvent.java336
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorEventAgent.java715
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorGnomonEcho.java225
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorInputAdaptor.java71
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorReadListener.java72
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/vp/OrbitBehavior.java1047
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/vp/ViewPlatformAWTBehavior.java400
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/vp/ViewPlatformBehavior.java137
-rw-r--r--src/classes/share/com/sun/j3d/utils/behaviors/vp/WandViewBehavior.java3843
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/CommandStream.java265
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/CompressedGeometryFile.java1007
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/CompressionStream.java2294
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/CompressionStreamColor.java270
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/CompressionStreamElement.java359
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/CompressionStreamNormal.java673
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/CompressionStreamVertex.java318
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/GeometryCompressor.java203
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/HuffmanNode.java223
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/HuffmanTable.java477
-rw-r--r--src/classes/share/com/sun/j3d/utils/compression/MeshBuffer.java238
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/BBox.java128
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Basic.java168
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/BottleNeck.java167
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Box.java469
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Bridge.java398
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Clean.java193
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/ColorCube.java175
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Cone.java428
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Cylinder.java421
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Degenerate.java170
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Desperate.java431
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Distance.java73
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/EarClip.java348
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Edge.java89
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/EdgeTable.java116
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/GeomBuffer.java557
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/GeometryInfo.java2826
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/GeometryInfoGenerator.java857
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Heap.java212
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/HeapNode.java75
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Left.java74
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/ListNode.java87
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/NoHash.java302
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/NormalGenerator.java907
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Numerics.java644
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Orientation.java173
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/PntNode.java70
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Primitive.java262
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Project.java254
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Quadrics.java722
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Simple.java225
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Sphere.java502
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Stripifier.java2535
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/StripifierStats.java345
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Text2D.java382
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Triangle.java69
-rw-r--r--src/classes/share/com/sun/j3d/utils/geometry/Triangulator.java1049
-rw-r--r--src/classes/share/com/sun/j3d/utils/image/TextureLoader.java779
-rw-r--r--src/classes/share/com/sun/j3d/utils/picking/PickCanvas.java248
-rw-r--r--src/classes/share/com/sun/j3d/utils/picking/PickIntersection.java1570
-rw-r--r--src/classes/share/com/sun/j3d/utils/picking/PickResult.java3344
-rw-r--r--src/classes/share/com/sun/j3d/utils/picking/PickTool.java1027
-rw-r--r--src/classes/share/com/sun/j3d/utils/picking/behaviors/PickMouseBehavior.java182
-rw-r--r--src/classes/share/com/sun/j3d/utils/picking/behaviors/PickRotateBehavior.java172
-rw-r--r--src/classes/share/com/sun/j3d/utils/picking/behaviors/PickTranslateBehavior.java157
-rw-r--r--src/classes/share/com/sun/j3d/utils/picking/behaviors/PickZoomBehavior.java155
-rw-r--r--src/classes/share/com/sun/j3d/utils/picking/behaviors/PickingCallback.java76
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/NamedObjectException.java68
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/ObjectNotLoadedException.java68
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphFileReader.java224
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphFileWriter.java157
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphIO.java112
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphObjectReferenceControl.java69
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphStreamReader.java117
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphStreamWriter.java127
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/UnresolvedBehavior.java64
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/UnsupportedUniverseException.java71
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/doc-files/extensibility.html189
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/package.html105
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/Controller.java943
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/J3fInputStream.java136
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/J3fOutputStream.java132
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/PositionInputStream.java98
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/PositionOutputStream.java91
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/RandomAccessFileControl.java500
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/SGIORuntimeException.java74
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/StreamControl.java205
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/SymbolTable.java851
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/SymbolTableData.java134
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/behaviors/interpolators/KBRotPosScaleSplinePathInterpolatorState.java131
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/behaviors/interpolators/RotPosScaleTCBSplinePathInterpolatorState.java128
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/behaviors/mouse/MouseBehaviorState.java85
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/BoxState.java155
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/ColorCubeState.java98
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/ConeState.java144
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/CylinderState.java143
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/PrimitiveState.java86
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/SphereState.java120
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/Text2DState.java137
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/image/ImageComponent2DURL.java122
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/image/ImageComponent2DURLIOListener.java71
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/image/ImageComponent2DURLState.java131
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/universe/PlatformGeometryState.java66
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/universe/SimpleUniverseState.java331
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/universe/ViewerAvatarState.java67
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/AlphaState.java100
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/AlternateAppearanceState.java126
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/AmbientLightState.java61
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/AppearanceState.java191
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/AuralAttributesState.java125
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BackgroundSoundState.java74
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BackgroundState.java125
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BehaviorState.java106
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BillboardState.java105
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BoundingLeafState.java87
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BranchGroupState.java63
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ClipState.java86
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ColorInterpolatorState.java114
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ColoringAttributesState.java84
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/CompressedGeometryState.java151
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ConeSoundState.java122
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DecalGroupState.java64
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DepthComponentFloatState.java111
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DepthComponentIntState.java111
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DepthComponentNativeState.java94
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DirectionalLightState.java77
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DistanceLODState.java108
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ExponentialFogState.java78
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/FogState.java108
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/Font3DState.java163
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/GeometryArrayState.java925
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/GeometryState.java59
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/GeometryStripArrayState.java81
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/GroupState.java142
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ImageComponent2DState.java114
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ImageComponent3DState.java139
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ImageComponentState.java385
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedGeometryArrayState.java176
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedGeometryStripArrayState.java81
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedLineArrayState.java92
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedLineStripArrayState.java86
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedPointArrayState.java93
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedQuadArrayState.java92
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedTriangleArrayState.java92
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedTriangleFanArrayState.java95
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedTriangleStripArrayState.java95
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/InterpolatorState.java88
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LODState.java95
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LeafState.java58
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LightState.java113
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LineArrayState.java76
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LineAttributesState.java87
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LineStripArrayState.java78
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LinearFogState.java80
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LinkState.java103
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/MaterialState.java96
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/MediaContainerState.java80
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ModelClipState.java133
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/MorphState.java134
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/NodeComponentState.java85
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/NodeState.java88
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/NullSceneGraphObjectState.java105
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/OrderedGroupState.java85
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/OrientedShape3DState.java96
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PathInterpolatorState.java81
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PointAttributesState.java81
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PointLightState.java79
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PointSoundState.java101
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PolygonAttributesState.java87
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PositionInterpolatorState.java88
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PositionPathInterpolatorState.java104
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/QuadArrayState.java86
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RasterState.java139
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RenderingAttributesState.java93
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RotPosPathInterpolatorState.java118
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RotPosScalePathInterpolatorState.java124
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RotationInterpolatorState.java90
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RotationPathInterpolatorState.java108
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ScaleInterpolatorState.java89
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SceneGraphObjectState.java506
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/Shape3DState.java121
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SharedGroupState.java63
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SoundState.java127
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SoundscapeState.java103
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SpotLightState.java81
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SwitchState.java82
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SwitchValueInterpolatorState.java104
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TexCoordGenerationState.java97
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/Text3DState.java115
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/Texture2DState.java140
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/Texture3DState.java103
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TextureAttributesState.java139
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TextureCubeMapState.java117
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TextureState.java231
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TextureUnitStateState.java111
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TransformGroupState.java91
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TransformInterpolatorState.java88
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TransparencyAttributesState.java86
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TransparencyInterpolatorState.java112
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TriangleArrayState.java92
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TriangleFanArrayState.java94
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TriangleStripArrayState.java95
-rw-r--r--src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ViewPlatformState.java83
-rw-r--r--src/classes/share/com/sun/j3d/utils/timer/J3DTimer.java102
-rw-r--r--src/classes/share/com/sun/j3d/utils/timer/package.html57
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/ConfigCommand.java417
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/ConfigContainer.java1479
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/ConfigDevice.java66
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/ConfigObject.java371
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/ConfigPhysicalBody.java178
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/ConfigPhysicalEnvironment.java179
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/ConfigScreen.java307
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/ConfigSensor.java211
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/ConfigSexpression.java569
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/ConfigView.java469
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/ConfigViewPlatform.java255
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/ConfigViewPlatformBehavior.java139
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/ConfiguredUniverse.java768
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/LocaleFactory.java82
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/MultiTransformGroup.java140
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/PlatformGeometry.java66
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/SimpleUniverse.java412
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/ViewInfo.java3398
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/Viewer.java1012
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/ViewerAvatar.java66
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/ViewingPlatform.java519
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/doc-files/config-examples.html85
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/doc-files/config-syntax.html1973
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x1-behavior.html109
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x1-stereo.html90
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x1-vr.html173
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x1-window.html70
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x1.html69
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x2-flat.html152
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x2-rot30.html111
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x3-cave-vr.html243
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x3-cave.html156
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x3-rot45.html122
-rw-r--r--src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d2x2-flat.html147
338 files changed, 101185 insertions, 0 deletions
diff --git a/src/classes/ToolsVersion b/src/classes/ToolsVersion
new file mode 100644
index 0000000..474fa2c
--- /dev/null
+++ b/src/classes/ToolsVersion
@@ -0,0 +1,9 @@
+Manifest-Version: 1.0
+Specification-Title:
+Specification-Version: 1.3
+Specification-Vendor: Sun Microsystems, Inc.
+Implementation-Title: Java 3D Utilities Runtime Environment
+Implementation-Version: 1.3.2
+Implementation-Vendor: Sun Microsystems, Inc.
+Extension-Name: javax.media.j3d
+Implementation-Vendor-Id: com.sun
diff --git a/src/classes/share/com/sun/j3d/ExceptionStrings.properties b/src/classes/share/com/sun/j3d/ExceptionStrings.properties
new file mode 100644
index 0000000..bf031b7
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/ExceptionStrings.properties
@@ -0,0 +1,68 @@
+Intersect0=Intersect.rayAndQuad : This quad has less than 4 points!
+Intersect1=Intersect.rayAndTriangle : This triangle has less than 3 points!
+Intersect3=Intersect.segmentAndQuad : This quad has less than 4 points!
+Intersect5=Intersect.segmentAndTriangle:This triange has less than 3 points!
+Intersect6=Intersect.segmentAndTriangle : This triange has less than 3 points!
+Intersect7=Intersect.pointAndquad : This quad has less than 4 points!
+Intersect9=Intersect.pointAndTriangle: This triange has less than 3 points!
+Intersect10=Intersect.pointAndTriangle : This triange has less than 3 points!
+Intersect11=Intersect.rayAndLine : This line has less than 2 points!
+Intersect13=Intersect.segmentAndLine : This line has less than 2 points!
+TCBKeyFrame0=TCBKeyFrame: tension value should be between -1 and 1
+TCBKeyFrame1=TCBKeyFrame: bias value should be between -1 and 1
+TCBKeyFrame2=TCBKeyFrame: continuity value should be between -1 and 1
+TCBSplinePathInterpolator0=TCBSplinePathInterpolator: need at least 2 Key Frames for the interpolator
+TCBSplinePathInterpolator1=TCBSplinePathInterpolator: first key frame should have knot value of 0.0
+TCBSplinePathInterpolator2=TCBSplinePathInterpolator: last key frame should have knot value of 1.0
+TCBSplinePathInterpolator3=TCBSplinePathInterpolator: Key Frame knot values not in sequence
+CubicSplineCurve0=CubicSplineCurve needs at least 4 key frames
+KBKeyFrame0=KBKeyFrame: tension value should be between -1 and 1
+KBKeyFrame1=KBKeyFrame: bias value should be between -1 and 1
+KBKeyFrame2=KBKeyFrame: continuity value should be between -1 and 1
+KBSplinePathInterpolator0=KBSplinePathInterpolator: need at least 2 Key Frames for the interpolator
+KBSplinePathInterpolator1=KBSplinePathInterpolator: first key frame should have knot value of 0.0
+KBSplinePathInterpolator2=KBSplinePathInterpolator: last key frame should have knot value of 1.0
+KBSplinePathInterpolator3=KBSplinePathInterpolator: Key Frame knot values not in sequence
+KBCubicSplineCurve0=KBCubicSplineCurve needs at least 4 key frames
+GeometryInfo0=Illegal primitive.
+GeometryInfo1=Illegal use of deprecated setTextureCoordinateIndices(int[])
+GeometryInfo2=Length of float array not a multiple of dimensionality of texcoords
+GeometryInfo3=Coordinate data required.
+GeometryInfo4=Color Index list set with no color list set.
+GeometryInfo5=Index lists must all be the same length
+GeometryInfo6=StripCounts inconsistent with primitive
+GeometryInfo7=stripCounts sum inconsistent with number of vertices.
+GeometryInfo8=Sum of contourCounts must equal length of stripCounts array.
+GeometryInfo9=Data must be one of Point3f, Color3f, Color4f, Vector3f, TexCoord2f, TexCoord3f or TexCoord4f.
+GeometryInfo10=Missing Texture Coordinate data list
+GeometryInfo11=Normal Index list set with no normal list set.
+GeometryInfo12=For triangles, number of vertices must be multiple of 3.
+GeometryInfo13=For quads, number of vertices must be multiple of 4.
+GeometryInfo14=contourCounts only useful when primitive is POLYGON_ARRAY.
+GeometryInfo15=2D texture coordinates not specified.
+GeometryInfo16=3D texture coordinates not specified.
+GeometryInfo17=4D texture coordinates not specified.
+GeometryInfo18=Invalid texture coordinate set index.
+GeometryInfo19=Missing Index List.
+GeometryInfo20=Non-coordinate index list set in USE_COORD_INDEX_ONLY mode
+GeometryInfo21=setTextureCoordinateParams not called
+GeometryInfoGenerator0=Unsupported geometry type
+Triangulator0=GeometryInfo must have primitive type POLYGON_ARRAY.
+ViewingPlatform0=Multiple Viewer support not implemented
+ViewingPlatform1=TransformGroup does not exist
+DistanceAttenuation0=Distance attenuation array null
+VrmlParser0=VRML binary parser disabled
+LwsMotion0=Number of motion channels != 9!
+LwoSurface0=VSPC problem
+LwoParser0=File not of FORM-length-LWOB format
+LwsEnvelope0=Number of envelope channels != 1!
+SwitchPathInterpolator0=SwitchPathInterpolator: length of knots and numChildren must be equal
+FloatValueInterpolator0=FloatValueInterpolator: first knot is not 0.0
+FloatValueInterpolator1=FloatValueInterpolator: last knot is not 1.0
+FloatValueInterpolator2=FloatValueInterpolator: knot values out of order
+FloatValueInterpolator3=FloatValueInterpolator: number of knots and values must be equal
+JavaSoundMixer0=JavaSoundMixer.prepareSound - bad URL
+Behavior0=Cannot call addListener on a Behavior that was not created as a listener.
+Stripifier0=Cannot getStripifierStats on a Stripifier object that was not created with the COLLECT_STATS flag.
+OrbitBehavior0=Specified function must be one of ROTATE, TRANSLATE or ZOOM.
+OrbitBehavior1=Minimum Orbit radius must be > 0.0.
diff --git a/src/classes/share/com/sun/j3d/audioengines/AudioEngine.java b/src/classes/share/com/sun/j3d/audioengines/AudioEngine.java
new file mode 100644
index 0000000..8d4d36f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/audioengines/AudioEngine.java
@@ -0,0 +1,208 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.audioengines;
+
+import javax.media.j3d.*;
+
+/**
+ * The AudioEngine Class defines an audio output device that generates
+ * sound 'image' from scene graph.
+ * An AudioEngine object encapsulates the AudioDevice's basic information.
+ *
+ * <p>
+ * NOTE: AudioEngine developers should not subclass this class directly.
+ * Subclass AudioEngine3DL2 instead.
+ */
+public class AudioEngine implements AudioDevice {
+
+ /*
+ * This device's UNIX file descriptor
+ */
+ int fileDescriptor;
+
+ /*
+ * Type of audio output device J3D sound is played over:
+ * HEADPHONE, MONO_SPEAKER, STEREO_SPEAKERS
+ */
+ int audioPlaybackType = HEADPHONES;
+
+ /*
+ * Distance from center ear (midpoint between ears) to physical speaker.
+ * Default reflects distance for headphones.
+ * For two speakers it is assumed that the speakers are the same
+ * distance from the listener and that
+ */
+ float distanceToSpeaker = 0.0f;
+
+ /*
+ * Angle between the vector from center ear parallel to head coordiate
+ * Z axis and the vector from the center ear to the speaker.
+ * For two speakers it is assumed that the speakers are placed at the
+ * same angular offset from the listener.
+ */
+ float angleOffsetToSpeaker = 0.0f;
+
+ /*
+ * Channels currently available
+ */
+ int channelsAvailable = 8;
+
+ /*
+ * Total number of Channels ever available
+ */
+ int totalChannels = 8;
+
+ /**
+ * Construct a new AudioEngine with the specified P.E.
+ * @param physicalEnvironment the physical environment object where we
+ * want access to this device.
+ */
+ public AudioEngine(PhysicalEnvironment physicalEnvironment ) {
+ physicalEnvironment.setAudioDevice(this);
+ }
+
+ /**
+ * Code to initialize the device
+ * @return flag: true is initialized sucessfully, false if error
+ */
+ public boolean initialize() {
+ // Expected to be over-ridden by extending class
+ return true;
+ }
+
+ /**
+ * Code to close the device
+ * @return flag: true is closed sucessfully, false if error
+ */
+ public boolean close() {
+ // Expected to be over-ridden by extending class
+ return true;
+ }
+
+ /*
+ * Audio Playback Methods
+ */
+ /**
+ * Set Type of Audio Playback physical transducer(s) sound is output to.
+ * Valid types are HEADPHONE, MONO_SPEAKER, STEREO_SPEAKERS
+ * @param type of audio output device
+ */
+ public void setAudioPlaybackType(int type) {
+ audioPlaybackType = type;
+ }
+
+ /**
+ * Get Type of Audio Playback Output Device
+ * returns audio playback type to which sound is currently output
+ */
+ public int getAudioPlaybackType() {
+ return audioPlaybackType;
+ }
+
+ /**
+ * Set Distance from the Center Ear to a Speaker
+ * @param distance from the center ear and to the speaker
+ */
+ public void setCenterEarToSpeaker(float distance) {
+ distanceToSpeaker = distance;
+ }
+
+ /**
+ * Get Distance from Ear to Speaker
+ * returns value set as distance from listener's ear to speaker
+ */
+ public float getCenterEarToSpeaker() {
+ return distanceToSpeaker;
+ }
+
+ /**
+ * Set Angle Offset To Speaker
+ * @param angle in radian between head coordinate Z axis and vector to speaker */
+ public void setAngleOffsetToSpeaker(float angle) {
+ angleOffsetToSpeaker = angle;
+ }
+
+ /**
+ * Get Angle Offset To Speaker
+ * returns value set as angle between vector to speaker and Z head axis
+ */
+ public float getAngleOffsetToSpeaker() {
+ return angleOffsetToSpeaker;
+ }
+
+ /**
+ * Query total number of channels available for sound rendering
+ * for this audio device.
+ * returns number of maximum sound channels you can run with this
+ * library/device-driver.
+ */
+ public int getTotalChannels() {
+ // this method should be overridden by a device specific implementation
+ return (totalChannels);
+ }
+
+ /**
+ * Query number of channels currently available for use by the
+ * returns number of sound channels currently available (number
+ * not being used by active sounds.
+ */
+ public int getChannelsAvailable() {
+ return (channelsAvailable);
+ }
+
+ /**
+ * Query number of channels that would be used to render a particular
+ * sound node.
+ * @param sound refenence to sound node that query to be performed on
+ * returns number of sound channels used by a specific Sound node
+ * @deprecated This method is now part of the Sound class
+ */
+ public int getChannelsUsedForSound(Sound sound) {
+ if (sound != null)
+ return sound.getNumberOfChannelsUsed();
+ else
+ return -1;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/audioengines/AudioEngine3D.java b/src/classes/share/com/sun/j3d/audioengines/AudioEngine3D.java
new file mode 100644
index 0000000..2e495d5
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/audioengines/AudioEngine3D.java
@@ -0,0 +1,497 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.audioengines;
+
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import java.util.ArrayList;
+
+
+/**
+ * The AudioEngine3D Class defines an audio output device that generates
+ * sound 'image' from high-level sound parameters passed to it during
+ * scene graph.
+ *
+ * <P>
+ * The methods in this class are meant to be optionally overridden by an
+ * extended class. This extended class would provice device specific code.
+ *
+ * <P>
+ * Error checking on all parameters passed to these methods is already
+ * explicitly being done by the Java 3D core code that calls these methods.
+ *
+ * <p>
+ * NOTE: AudioEngine developers should not subclass this class directly.
+ * Subclass AudioEngine3DL2 instead.
+ */
+
+public class AudioEngine3D extends AudioEngine implements AudioDevice3D
+{
+ /*
+ * Identifiers of sample associated with sound source
+ * This array grows as the AudioDevice3D implementation requires it larger.
+ */
+ protected ArrayList samples = new ArrayList(64);
+
+ /**
+ * Current View sound is being rendered
+ */
+ protected View currentView = (View)null;
+
+ /*
+ * current Aural attribute Parameters
+ */
+ protected AuralParameters attribs = new AuralParameters();
+
+ /**
+ * Construct a new AudioEngine with the specified PhysicalEnvironment.
+ * @param physicalEnvironment the physical environment object where we
+ * want access to this device.
+ */
+ public AudioEngine3D(PhysicalEnvironment physicalEnvironment ) {
+ super(physicalEnvironment);
+ }
+
+ /*
+ *
+ * Methods that affect AudioEngine3D fields that are NOT associated
+ * with a specific sound sample
+ *
+ */
+
+ /**
+ * Save a reference to the current View object.
+ * @param reference to current view object
+ */
+ public void setView(View reference) {
+ currentView = reference;
+ return;
+ }
+ /**
+ * Get reference to the current View object.
+ * @return reference to current view object
+ */
+ public View getView() {
+ return (currentView);
+ }
+
+ /*
+ *
+ * Methods explicitly affect sound rendering and that require
+ * audio device specific methods that override this class.
+ *
+ */
+
+ /**
+ * Prepare Sound in device.
+ * Makes sound assessible to device - in this case attempts to load sound
+ * Stores sound type and data.
+ * @param soundType denotes type of sound: Background, Point or Cone
+ * @param soundData descrition of sound source data
+ * @return index into sample vector of Sample object for sound
+ */
+ public int prepareSound(int soundType, MediaContainer soundData) {
+ // This method must be overridden by device specific implementation
+ return Sample.NULL_SAMPLE;
+ }
+
+ /**
+ * Clear Sound.
+ * Removes/clears associated sound data with this sound source node
+ * @param index device specific reference number to device driver sample
+ */
+ public void clearSound(int index) {
+ // This method must be overridden by device specific implementation
+ return;
+ }
+
+ /**
+ * Set the transform for local to virtual world coordinate space
+ * @param index device specific reference number to device driver sample
+ * @param trans is a reference to virtual world composite transform
+ */
+ public void setVworldXfrm(int index, Transform3D trans) {
+ Sample sample = (Sample)getSample(index);
+ if (sample != null)
+ sample.vworldXfrm.set(trans);
+ return;
+ }
+ /**
+ * Start sample playing on audio device
+ * @param index device specific reference number to device driver sample
+ * @return status: < 0 denotes an error
+ */
+ public int startSample(int index) {
+ // This method must be overridden by device specific implementation
+ return -1; // error if not overridden
+ }
+
+ /**
+ * Stop sample playing on audio device
+ * @param index device specific reference number to device driver sample
+ * @return status: < 0 denotes an error
+ */
+ public int stopSample(int index) {
+ // This method must be overridden by device specific implementation
+ return -1; // error if not overridden
+ }
+
+ /**
+ * Update sample.
+ * Implies that some parameters affecting rendering have been modified.
+ * @param index device specific reference number to device driver sample
+ */
+ // TODO: The update method exists on a TEMPORARY basis.
+ public void updateSample(int index) {
+ // This method must be overridden by device specific implementation
+ return;
+ }
+
+ /**
+ * Mute sample.
+ * @param index device specific reference number to device driver sample
+ */
+ public void muteSample(int index) {
+ // This method must be overridden by device specific implementation
+ return;
+ }
+
+ /**
+ * Unmute sample.
+ * @param index device specific reference number to device driver sample
+ */
+ public void unmuteSample(int index) {
+ // This method must be overridden by device specific implementation
+ return;
+ }
+
+ /**
+ * Pause sample.
+ * @param index device specific reference number to device driver sample
+ */
+ public void pauseSample(int index) {
+ // This method must be overridden by device specific implementation
+ return;
+ }
+
+ /**
+ * Unpause sample.
+ * @param index device specific reference number to device driver sample
+ */
+ public void unpauseSample(int index) {
+ // This method must be overridden by device specific implementation
+ return;
+ }
+
+ /*
+ *
+ * Methods that affect fields associated with the sound sample
+ * and that may cause implicit rendering.
+ *
+ */
+ /**
+ * Set gain scale factor applied to sample.
+ * @param index device specific reference number to device driver sample
+ * @param scaleFactor floating point multiplier applied to sample amplitude
+ */
+ public void setSampleGain(int index, float scaleFactor) {
+ Sample sample = (Sample)getSample(index);
+ if (sample != null)
+ sample.setGain(scaleFactor);
+ return;
+ }
+
+ /**
+ * Set number of times sample is looped.
+ * @param index device specific reference number to device driver sample
+ * @param count number of times sample is repeated
+ */
+ public void setLoop(int index, int count) {
+ Sample sample = (Sample)getSample(index);
+ if (sample != null)
+ sample.setLoopCount(count);
+ return;
+ }
+
+ /**
+ * Set location of sample.
+ * @param index device specific reference number to device driver sample
+ * @param position point location in virtual world coordinate of sample
+ */
+ public void setPosition(int index, Point3d position) {
+ Sample sample = (Sample)getSample(index);
+ if (sample != null)
+ sample.setPosition(position);
+ return;
+ }
+
+ /* Set elliptical distance attenuation arrays applied to sample amplitude.
+ * @param index device specific reference number to device driver sample
+ * @param frontDistance defines an array of distance along the position axis
+ * thru which ellipses pass
+ * @param frontAttenuationScaleFactor gain scale factors
+ * @param backDistance defines an array of distance along the negative axis
+ * thru which ellipses pass
+ * @param backAttenuationScaleFactor gain scale factors
+ */
+ public void setDistanceGain(int index,
+ double[] frontDistance, float[] frontAttenuationScaleFactor,
+ double[] backDistance, float[] backAttenuationScaleFactor) {
+ Sample sample = (Sample)getSample(index);
+ if (sample != null)
+ sample.setDistanceGain(frontDistance, frontAttenuationScaleFactor,
+ backDistance, backAttenuationScaleFactor);
+ return;
+ }
+
+ /**
+ * Set direction vector of sample.
+ * @param index device specific reference number to device driver sample
+ * @param direction vector in virtual world coordinate.
+ */
+ public void setDirection(int index, Vector3d direction) {
+ Sample sample = (Sample)getSample(index);
+ if (sample != null)
+ sample.setDirection(direction);
+ return;
+ }
+
+ /**
+ * Set angular attenuation arrays affecting angular amplitude attenuation
+ * and angular distance filtering.
+ * @param index device specific reference number to device driver sample
+ * @param filterType denotes type of filtering (on no filtering) applied
+ * to sample.
+ * @param angle array containing angular distances from sound axis
+ * @param attenuationScaleFactor array containing gain scale factor
+ * @param filterCutoff array containing filter cutoff frequencies.
+ * The filter values for each tuples can be set to Sound.NO_FILTER.
+ */
+ public void setAngularAttenuation(int index, int filterType,
+ double[] angle, float[] attenuationScaleFactor, float[] filterCutoff) {
+ Sample sample = (Sample)getSample(index);
+ if (sample != null)
+ sample.setAngularAttenuation(filterType, angle,
+ attenuationScaleFactor, filterCutoff);
+ return;
+ }
+
+ /**
+ * Set rolloff value for current aural attribute applied to all samples.
+ * @param rolloff scale factor applied to standard speed of sound.
+ */
+ public void setRolloff(float rolloff) {
+ attribs.rolloff = rolloff;
+ return;
+ }
+
+ /**
+ * Set reverberation surface reflection coefficient value for current aural
+ * attribute applied to all samples.
+ * @param coefficient applied to amplitude of reverbation added at each
+ * iteration of reverb processing.
+ */
+ public void setReflectionCoefficient(float coefficient) {
+ attribs.reflectionCoefficient = coefficient;
+ return;
+ }
+
+ /**
+ * Set reverberation delay time for current aural attribute applied to
+ * all samples.
+ * @param reverbDelay amount of time in millisecond between each
+ * iteration of reverb processing.
+ */
+ public void setReverbDelay(float reverbDelay) {
+ attribs.reverbDelay = reverbDelay;
+ return;
+ }
+
+ /**
+ * Set reverberation order for current aural attribute applied to all
+ * samples.
+ * @param reverbOrder number of times reverb process loop is iterated.
+ */
+ public void setReverbOrder(int reverbOrder) {
+ attribs.reverbOrder = reverbOrder;
+ return;
+ }
+
+ /**
+ * Set distance filter for current aural attribute applied to all samples.
+ * @param filterType denotes type of filtering (on no filtering) applied
+ * to all sample based on distance between listener and sound.
+ * @param dist is an attenuation array of distance and low-pass filter values.
+ */
+ public void setDistanceFilter(int filterType,
+ double[] dist, float[] filterCutoff) {
+ attribs.setDistanceFilter(filterType, dist, filterCutoff);
+ return;
+ }
+
+ /**
+ * Set frequency scale factor for current aural attribute applied to all
+ * samples.
+ * @param scaleFactor frequency scale factor applied to samples normal
+ * playback rate.
+ */
+ public void setFrequencyScaleFactor(float scaleFactor) {
+ attribs.frequencyScaleFactor = scaleFactor;
+ return;
+ }
+ /**
+ * Set velocity scale factor for current aural attribute applied to all
+ * samples when Doppler is calculated.
+ * @param scaleFactor scale factor applied to postional samples'
+ * listener-to-soundSource velocity.
+ * playback rate.
+ */
+ public void setVelocityScaleFactor(float scaleFactor) {
+ attribs.velocityScaleFactor = scaleFactor;
+ return;
+ }
+
+ /**
+ * Get number of channels used by a particular sample on the audio device.
+ * @param index device specific reference number to device driver sample
+ * @return number of channels currently being used by this sample.
+ */
+ public int getNumberOfChannelsUsed(int index) {
+ // This method must be overridden by device specific implementation
+ Sample sample = (Sample)getSample(index);
+ if (sample != null)
+ return (sample.getNumberOfChannelsUsed());
+ else
+ return 0;
+ }
+
+ /**
+ * Get number of channels that would be used by a particular sample on
+ * the audio device given the mute flag passed in as a parameter.
+ * @param index device specific reference number to device driver sample
+ * @param muteFlag denotes the mute state to assume while executing this
+ * query. This mute value does not have to match the current mute state
+ * of the sample.
+ * @return number of channels that would be used by this sample if it
+ * were playing.
+ */
+ public int getNumberOfChannelsUsed(int index, boolean muteFlag) {
+ // This method must be overridden by device specific implementation
+ Sample sample = (Sample)getSample(index);
+ if (sample != null)
+ return (sample.getNumberOfChannelsUsed());
+ else
+ return 0;
+ }
+
+ /**
+ * Get length of time a sample would play if allowed to play to completion.
+ * @param index device specific reference number to device driver sample
+ * @return length of sample in milliseconds
+ */
+ public long getSampleDuration(int index) {
+ Sample sample = (Sample)getSample(index);
+ if (sample != null)
+ return (sample.getDuration());
+ else
+ return 0L;
+ }
+
+ /**
+ * Get time this sample begun playing on the audio device.
+ * @param index device specific reference number to device driver sample
+ * @return system clock time sample started
+ */
+ public long getStartTime(int index) {
+ Sample sample = (Sample)getSample(index);
+ if (sample != null)
+ return (sample.getStartTime());
+ else
+ return 0L;
+ }
+
+ /**
+ * Get reference to the array list of samples
+ * @return reference to samples list
+ * @deprecated unsafe to get reference to samples list with this method.
+ * It's better to directly reference samples list within a synchronized
+ * block which also contains calls to .getSample(index).
+ */
+ protected ArrayList getSampleList() {
+ return (samples);
+ }
+
+ public int getSampleListSize() {
+ return (samples.size());
+ }
+
+ /**
+ * Get specific sample from indexed sample list
+ * Checks for valid index before attempting to get sample from list.
+ * @param index device specific reference number to device driver sample
+ * @return reference to sample; returns null if index out of range.
+ *
+ * @since Java 3D 1.2.1
+ */
+ public Sample getSample(int index) {
+ synchronized(samples) {
+ if ((index >= 0) && (index < samples.size())) {
+ Sample sample = (Sample)samples.get(index);
+ return (sample);
+ }
+ else
+ return null;
+ }
+ }
+
+ /*
+ * Get reference to current aural attribute parameters associated with
+ * this audio device.
+ * @return reference to current aural attribute parameters
+ */
+ public AuralParameters getAuralParameters() {
+ return (attribs);
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/audioengines/AudioEngine3DL2.java b/src/classes/share/com/sun/j3d/audioengines/AudioEngine3DL2.java
new file mode 100644
index 0000000..c8865c2
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/audioengines/AudioEngine3DL2.java
@@ -0,0 +1,308 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.audioengines;
+
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import java.util.ArrayList;
+
+
+/**
+ * The AudioEngine3DL2 Class defines an audio output device that generates
+ * sound 'image' from high-level sound parameters passed to it during
+ * scene graph.
+ *
+ * <P>
+ * The methods in this class are meant to be optionally overridden by an
+ * extended class. This extended class would provice device specific code.
+ *
+ * <P>
+ * Error checking on all parameters passed to these methods is already
+ * explicitly being done by the Java 3D core code that calls these methods.
+ *
+ * <P>
+ * These methods should NOT be called by any application if the audio engine
+ * is associated with a Physical Environment used by Java3D Core.
+ *
+ * @since Java 3D 1.3
+ */
+public class AudioEngine3DL2 extends AudioEngine3D implements AudioDevice3DL2 {
+ /**
+ * Construct a new AudioEngine3DL2 with the specified PhysicalEnvironment.
+ * @param physicalEnvironment the physical environment object where we
+ * want access to this device.
+ */
+ public AudioEngine3DL2(PhysicalEnvironment physicalEnvironment ) {
+ super(physicalEnvironment);
+ }
+
+ /*
+ *
+ * Methods that affect AudioEngine3DLD fields that are NOT associated
+ * with a specific sound sample
+ *
+ */
+
+ /**
+ * Pauses audio device engine without closing the device and associated
+ * threads.
+ * Causes all cached sounds to be paused and all streaming sounds to be
+ * stopped.
+ */
+ public void pause() {
+ // This method must be overridden by device specific implementation
+ return;
+ }
+ /**
+ * Resumes audio device engine (if previously paused) without
+ * reinitializing the device.
+ * Causes all paused cached sounds to be resumed and all streaming
+ * sounds restarted.
+ */
+ public void resume() {
+ // This method must be overridden by device specific implementation
+ return;
+ }
+
+ /**
+ * Set overall gain control of all sounds playing on the audio device.
+ * @param scaleFactor scale factor applied to calculated amplitudes for
+ * all sounds playing on this device
+ */
+ public void setGain(float scaleFactor) {
+ // This method must be overridden by device specific implementation
+ return;
+ }
+
+
+ /*
+ *
+ * Methods explicitly affect a particular sound rendering and that
+ * require audio device specific methods that override this class.
+ *
+ */
+
+ /**
+ * Set scale factor applied to sample playback rate for a particular sound
+ * associated with the audio device.
+ * Changing the device sample rate affects both the pitch and speed.
+ * This scale factor is applied to ALL sound types.
+ * Changes (scales) the playback rate of a sound independent of
+ * Doppler rate changes.
+ * @param index device specific reference to device driver sample
+ * @param scaleFactor non-negative factor applied to calculated
+ * amplitudes for all sounds playing on this device
+ * @see Sound#setRateScaleFactor
+ */
+ public void setRateScaleFactor(int index, float scaleFactor) {
+ Sample sample = (Sample)getSample(index);
+ if (sample != null)
+ sample.setRateScaleFactor(scaleFactor);
+ return;
+ }
+
+
+ /*
+ *
+ * Methods explicitly affect aural attributes of the listening space
+ * used to calculated reverberation during sound rendering.
+ * These require audio device specific methods that override this class.
+ *
+ */
+
+ /**
+ * Set late reflection (referred to as 'reverb') attenuation.
+ * This scale factor is applied to iterative, indistinguishable
+ * late reflections that constitute the tail of reverberated sound in
+ * the aural environment.
+ * This parameter, along with the early reflection coefficient, defines
+ * the reflective/absorptive characteristic of the surfaces in the
+ * current listening region.
+ * @param coefficient late reflection attenuation factor
+ * @see AuralAttributes#setReverbCoefficient
+ */
+ public void setReverbCoefficient(float coefficient) {
+ attribs.reverbCoefficient = coefficient;
+ return;
+ }
+
+
+ /**
+ * Sets the early reflection delay time.
+ * In this form, the parameter specifies the delay time between each order
+ * of reflection (while reverberation is being rendered) explicitly given
+ * in milliseconds.
+ * @param reflectionDelay time between each order of early reflection
+ * @see AuralAttributes#setReflectionDelay
+ */
+ public void setReflectionDelay(float reflectionDelay) {
+ attribs.reflectionDelay = reflectionDelay;
+ return;
+ }
+
+ /**
+ * Set reverb decay time.
+ * Defines the reverberation decay curve.
+ * @param time decay time in milliseconds
+ * @see AuralAttributes#setDecayTime
+ */
+ public void setDecayTime(float time) {
+ attribs.decayTime = time;
+ return;
+ }
+
+ /**
+ * Set reverb decay filter.
+ * This provides for frequencies above the given cutoff frequency to be
+ * attenuated during reverb decay at a different rate than frequencies
+ * below this value. Thus, defining a different reverb decay curve for
+ * frequencies above the cutoff value.
+ * @param frequencyCutoff value of frequencies in Hertz above which a
+ * low-pass filter is applied.
+ * @see AuralAttributes#setDecayFilter
+ */
+ public void setDecayFilter(float frequencyCutoff) {
+ attribs.decayFrequencyCutoff = frequencyCutoff;
+ return;
+ }
+
+ /**
+ * Set reverb diffusion.
+ * This defines the echo dispersement (also referred to as 'echo density').
+ * The value of this reverb parameter is expressed as a percent of the
+ * audio device's minimum-to-maximum values.
+ * @param diffusion percentage expressed within the range of 0.0 and 1.0
+ * @see AuralAttributes#setDiffusion
+ */
+ public void setDiffusion(float diffusion) {
+ attribs.diffusion = diffusion;
+ return;
+ }
+
+ /**
+ * Set reverb density.
+ * This defines the modal density (also referred to as 'spectral
+ * coloration').
+ * The value of this parameter is expressed as a percent of the audio
+ * device's minimum-to-maximum values for this reverb parameter.
+ * @param density reverb density expressed as a percentage,
+ * within the range of 0.0 and 1.0
+ * @see AuralAttributes#setDensity
+ */
+ public void setDensity(float density) {
+ attribs.density = density;
+ return;
+ }
+
+
+ /**
+ * Set the obstruction gain control. This method allows for attenuating
+ * sound waves traveling between the sound source and the listener
+ * obstructed by objects. Direct sound signals/waves for obstructed sound
+ * source are attenuated but not indirect (reflected) waves.
+ * There is no corresponding Core AuralAttributes method at this time.
+ * @param index device specific reference to device driver sample
+ * @param scaleFactor non-negative factor applied to direct sound gain
+ */
+ public void setObstructionGain(int index, float scaleFactor) {
+ Sample sample = (Sample)getSample(index);
+ if (sample != null)
+ sample.setObstructionGain(scaleFactor);
+ return;
+ }
+
+ /**
+ * Set the obstruction filter control.
+ * This provides for frequencies above the given cutoff frequency
+ * to be attenuated, during while the gain of an obstruction signal
+ * is being calculated, at a different rate than frequencies
+ * below this value.
+ * There is no corresponding Core AuralAttributes method at this time.
+ * @param index device specific reference to device driver sample
+ * @param frequencyCutoff value of frequencies in Hertz above which a
+ * low-pass filter is applied.
+ */
+
+ public void setObstructionFilter(int index, float frequencyCutoff) {
+ Sample sample = (Sample)getSample(index);
+ if (sample != null)
+ sample.setObstructionFilter(frequencyCutoff);
+ return;
+ }
+
+ /**
+ * Set the occlusion gain control. This method allows for attenuating
+ * sound waves traveling between the sound source and the listener
+ * occluded by objects. Both direct and indirect sound signals/waves
+ * for occluded sound sources are attenuated.
+ * There is no corresponding Core AuralAttributes method at this time.
+ * @param index device specific reference to device driver sample
+ * @param scaleFactor non-negative factor applied to direct sound gain
+ */
+ public void setOcclusionGain(int index, float scaleFactor) {
+ Sample sample = (Sample)getSample(index);
+ if (sample != null)
+ sample.setObstructionGain(scaleFactor);
+ return;
+ }
+
+ /**
+ * Set the occlusion filter control.
+ * This provides for frequencies above the given cutoff frequency
+ * to be attenuated, during while the gain of an occluded signal
+ * is being calculated, at a different rate than frequencies below
+ * this value.
+ * There is no corresponding Core AuralAttributes method at this time.
+ * @param index device specific reference to device driver sample
+ * @param frequencyCutoff value of frequencies in Hertz above which a
+ * low-pass filter is applied.
+ */
+ public void setOcclusionFilter(int index, float frequencyCutoff) {
+ Sample sample = (Sample)getSample(index);
+ if (sample != null)
+ sample.setObstructionFilter(frequencyCutoff);
+ return;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/audioengines/AudioEngineThread.java b/src/classes/share/com/sun/j3d/audioengines/AudioEngineThread.java
new file mode 100644
index 0000000..bf67e0a
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/audioengines/AudioEngineThread.java
@@ -0,0 +1,275 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.audioengines;
+
+/*
+ * Audio Engine Thread
+ */
+
+import javax.media.j3d.*;
+
+/**
+ * The Thread Class extended for Audio Device engines that must process
+ * calls dynamically, in 'real-time" to asynchronously change engine
+ * parameters.
+ *
+ * <p>
+ * NOTE: this class is probably not needed for those Audio Device implementations
+ * that handle all dynamic parameters in the low-level audio library.
+ */
+public class AudioEngineThread extends Thread {
+
+ // Debug print flag
+ static final protected boolean debugFlag = false;
+
+
+ protected void debugPrint(String message) {
+ if (debugFlag)
+ System.out.println(message);
+ }
+
+ /**
+ * The classification types.
+ */
+ protected static final int WORK_THREAD = 0x01;
+ protected static final int UPDATE_THREAD = 0x02;
+
+ /**
+ * This runMonitor action puts the thread into an initial wait state
+ */
+ protected static final int WAIT = 0;
+
+ /**
+ * This runMonitor action notifies MasterControl that this thread
+ * has completed and wait.
+ */
+ protected static final int NOTIFY_AND_WAIT = 1;
+
+ /**
+ * This runMonitor action tells the thread to run N number of
+ * iterations.
+ */
+ protected static final int RUN = 2;
+
+ /**
+ * This runMonitor action tells the thread to stop running
+ */
+ protected static final int STOP = 3;
+
+ /**
+ * This indicates that this thread has been activated by MC
+ */
+ protected boolean active = false;
+
+ /**
+ * This indicates that this thread is alive and running
+ */
+ protected boolean running = true;
+
+
+ /**
+ * This indicates that this thread is ready
+ */
+ protected boolean started = false;
+
+ /**
+ * The time values passed into this thread
+ */
+ protected long referenceTime;
+
+ /**
+ * Use to assign threadOpts WAIT_ALL_THREADS
+ */
+ protected long lastWaitTimestamp = 0;
+
+ /**
+ * The type of this thread. It is one of the above constants.
+ */
+ protected int type;
+
+ /**
+ * The classification of this thread. It is one of the above constants.
+ */
+ protected int classification = WORK_THREAD;
+
+ /**
+ * The arguments passed in for this thread
+ */
+ protected Object[] args = null;
+
+ /**
+ * Flag to indicate that user initiate a thread stop
+ */
+ protected boolean userStop = false;
+
+ /**
+ * Flag to indicate that this thread is waiting to be notify
+ */
+ protected boolean waiting = false;
+
+ /**
+ * Some variables used to name threads correctly
+ */
+ protected static int numInstances = 0;
+ protected int instanceNum = -1;
+
+ /**
+ * This constructor simply assigns the given id.
+ */
+ public AudioEngineThread(ThreadGroup t, String threadName) {
+ super(t, threadName);
+ if (debugFlag)
+ debugPrint("AudioEngineThread.constructor("+threadName +")");
+ }
+
+ synchronized int newInstanceNum() {
+ return (++numInstances);
+ }
+
+ int getInstanceNum() {
+ if (instanceNum == -1)
+ instanceNum = newInstanceNum();
+ return instanceNum;
+ }
+
+ /**
+ * This method is defined by all slave threads to implement
+ * one iteration of work.
+ */
+ synchronized public void doWork() {
+ if (debugFlag)
+ debugPrint("AudioEngineThread.doWork()");
+ }
+
+ /**
+ * This initializes this thread. Once this method returns, the thread is
+ * ready to do work.
+ */
+ public void initialize() {
+ if (debugFlag)
+ debugPrint("AudioEngineThread.initialize()");
+ this.start();
+ while (!started) {
+ try {
+ Thread.currentThread().sleep(1, 0);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ /**
+ * This causes the threads run method to exit.
+ */
+ public void finish() {
+ while (!waiting) {
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) {}
+ }
+ runMonitor(STOP, 0,null);
+ }
+
+ /*
+ * This thread controls the syncing of all the canvases attached to
+ * this view.
+ */
+ public void run() {
+ if (debugFlag)
+ debugPrint("AudioEngineThread.run");
+ runMonitor(WAIT, 0, null);
+ while (running) {
+ doWork();
+ runMonitor(WAIT, 0, null);
+ }
+ // resource clean up
+ shutdown();
+ }
+
+ synchronized public void runMonitor(int action, long referenceTime, Object[] args){
+ switch (action) {
+ case WAIT:
+ if (debugFlag)
+ debugPrint("AudioEngineThread.runMonitor(WAIT)");
+ try {
+ started = true;
+ waiting = true;
+ wait();
+ } catch (InterruptedException e) {
+ System.err.println(e);
+ }
+ waiting = false;
+ break;
+ case RUN:
+ if (debugFlag)
+ debugPrint("AudioEngineThread.runMonitor(RUN)");
+ this.referenceTime = referenceTime;
+ this.args = args;
+ notify();
+ break;
+ case STOP:
+ if (debugFlag)
+ debugPrint("AudioEngineThread.runMonitor(STOP)");
+ running = false;
+ notify();
+ break;
+ }
+ }
+
+ public void shutdown() {
+ }
+
+ // default resource clean up method
+ public void cleanup() {
+ active = false;
+ running = true;
+ started = true;
+ lastWaitTimestamp = 0;
+ classification = WORK_THREAD;
+ args = null;
+ userStop = false;
+ referenceTime = 0;
+
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/audioengines/AuralParameters.java b/src/classes/share/com/sun/j3d/audioengines/AuralParameters.java
new file mode 100644
index 0000000..6c29164
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/audioengines/AuralParameters.java
@@ -0,0 +1,197 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.audioengines;
+
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+/**
+ * The AuralParameters Class defines a set of fields that define the
+ * Aural listening environment. Many of the parameters correspond to
+ * AuralAttribute fields.
+ *
+ * <p>
+ * Error checking on all parameters passed to these methods is already
+ * explicitly being done by the Java 3D core code that calls these methods.
+ */
+
+public class AuralParameters
+{
+ // Speed of Sound in meters/milliseconds
+ public static final float SPEED_OF_SOUND = 0.344f;
+ public static final int NO_FILTERING = -1;
+
+ public float rolloff = 1.0f;
+ public float reflectionCoefficient = 0.0f;
+ public float reverbDelay = 40.0f;
+ public int reverbOrder = 0;
+ public float frequencyScaleFactor = 1.0f;
+ public float velocityScaleFactor = 0.0f;
+ int filterType = NO_FILTERING;
+ double[] filterDistance = null;
+ float[] filterCutoff = null;
+
+ /*
+ * @since Java 3D 1.3
+ */
+ public float reverbCoefficient = 1.0f;
+ public float reflectionDelay = 20.0f;
+ public float decayTime = 1000.0f;
+ public float decayFrequencyCutoff = 5000.0f;
+ public float diffusion = 1.0f; // 100%
+ public float density = 1.0f; // 100%
+
+ /**
+ * Construct a new AuralParameters object
+ */
+ public AuralParameters() {
+ frequencyScaleFactor = 1.0f;
+ velocityScaleFactor = 0.0f;
+ rolloff = 1.0f;
+ reflectionCoefficient = 0.0f;
+ reflectionDelay = 20.0f;
+ reverbCoefficient = 1.0f;
+ reverbDelay = 40.0f;
+ reverbOrder = 0;
+ filterType = NO_FILTERING;
+ filterDistance = new double[2]; // start out with array of two
+ filterCutoff = new float[2]; // start out with array of two
+ decayTime = 1000.0f;
+ decayFrequencyCutoff = 5000.0f;
+ diffusion = 1.0f; // 100%
+ density = 1.0f; // 100%
+ }
+
+ public void setDistanceFilter(int filterType, double[] distance,
+ float[] filterCutoff) {
+ boolean error = false;
+ boolean allocate = false;
+ int attenuationLength = 0;
+ if (distance == null || filterCutoff == null) {
+ error = true;
+ }
+ else {
+ attenuationLength = distance.length;
+ if (attenuationLength == 0 || filterType == NO_FILTERING) {
+ error = true;
+ }
+ }
+ if (error) {
+ this.filterType = NO_FILTERING;
+ this.filterDistance = null;
+ this.filterCutoff = null;
+ if (debugFlag)
+ debugPrint("setDistanceFilter NO_FILTERING");
+ return;
+ }
+ this.filterType = filterType;
+ if (debugFlag)
+ debugPrint("setDistanceFilter type = " + filterType);
+ if ((filterDistance == null) || (filterCutoff == null)) {
+ allocate = true;
+ }
+ else if (attenuationLength > filterDistance.length) {
+ allocate = true;
+ }
+ if (allocate) {
+ if (debugFlag)
+ debugPrint("setDistanceFilter length = " + attenuationLength);
+ this.filterDistance = new double[attenuationLength];
+ this.filterCutoff = new float[attenuationLength];
+ }
+ System.arraycopy(distance, 0, this.filterDistance, 0,
+ attenuationLength);
+ System.arraycopy(filterCutoff, 0, this.filterCutoff, 0,
+ attenuationLength);
+
+ if (debugFlag) {
+ debugPrint("setDistanceFilter arrays = ");
+ for (int i=0; i<attenuationLength; i++)
+ debugPrint(this.filterDistance[i] + "," + this.filterCutoff[i]);
+ debugPrint("setDistanceFilter passed in = ");
+ for (int i=0; i<attenuationLength; i++)
+ debugPrint((float)(filterDistance[i]) + "," + filterCutoff[i]);
+ }
+ return;
+ }
+ public int getDistanceFilterLength() {
+ if (filterDistance != null)
+ return filterDistance.length;
+ return 0;
+ }
+
+ public int getDistanceFilterType() {
+ return filterType;
+ }
+
+ public void getDistanceFilter(double[] distance, float[] filterCutoff) {
+ if (distance == null || filterCutoff == null)
+ return;
+ int attenuationLength = distance.length;
+ if (attenuationLength == 0 ||
+ (filterDistance==null) || (filterCutoff==null))
+ return;
+ if (attenuationLength > filterDistance.length)
+ attenuationLength = filterDistance.length;
+ System.arraycopy(this.filterDistance, 0, distance, 0,
+ attenuationLength);
+ System.arraycopy(this.filterCutoff, 0, filterCutoff, 0,
+ attenuationLength);
+ return;
+ }
+
+ // Debug print flags
+ static final boolean debugFlag = false;
+ static final boolean internalErrors = false;
+
+ /**
+ * Debug print method for Sound nodes
+ */
+ protected void debugPrint(String message) {
+ if (debugFlag)
+ System.out.println(message);
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/audioengines/Sample.java b/src/classes/share/com/sun/j3d/audioengines/Sample.java
new file mode 100644
index 0000000..620c28e
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/audioengines/Sample.java
@@ -0,0 +1,479 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.audioengines;
+
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+/**
+ * The Sample class defines the data and methods associated with a sound
+ * sample played through the AudioDevice.
+ * This contains all the data fields for non-spatialized and spatialized
+ * (positional and directional) sound samples.
+ */
+public class Sample {
+
+ // Debug print flags and methods
+ static final protected boolean debugFlag = false;
+ static final protected boolean internalErrors = false;
+
+ protected void debugPrint(String message) {
+ if (debugFlag)
+ System.out.println(message);
+ }
+
+ protected void debugPrintln(String message) {
+ if (debugFlag)
+ System.out.println(message);
+ }
+
+ /**
+ * Null Sound identifier denotes sound is not created or initialized
+ */
+ public static final int NULL_SAMPLE = -1;
+
+ /**
+ * sound data associated with sound source
+ */
+ protected MediaContainer soundData = null;
+
+ /**
+ * sound data associated with sound source
+ */
+ protected int soundType = -1;
+
+ /**
+ * Overall Scale Factor applied to sound gain.
+ */
+ protected float gain = 1.0f; // Valid values are >= 0.0.
+
+ /**
+ * Overall Scale Factor applied to sound.
+ * @since Java 3D 1.3
+ */
+ protected float rateScaleFactor = 1.0f; // Valid values are >= 0.0.
+
+ /**
+ * Number of times sound is looped/repeated during play
+ */
+ protected int loopCount = 0; // Range from 0 to POSITIVE_INFINITY(-1)
+
+
+ /*
+ * Duration of sample
+ * This should match the Sound node constant of same name
+ */
+ public static final int DURATION_UNKNOWN = -1;
+ protected long duration = DURATION_UNKNOWN;
+
+ protected int numberOfChannels = 0;
+ protected boolean mute = false; // denotes if sample is muted
+ // (playing with zero gain)
+
+ /*
+ *
+ * Fields associated with positional sound samples
+ *
+ */
+ /*
+ * Local to Vworld transform
+ */
+ protected Transform3D vworldXfrm = new Transform3D();
+ protected boolean vwXfrmFlag = false;
+
+ /*
+ * Origin of Sound source in Listener's space.
+ */
+ protected Point3f position = new Point3f(0.0f, 0.0f, 0.0f);
+
+ /*
+ * Pairs of distances and gain scale factors that define piecewise linear
+ * gain attenuation between each pair.
+ */
+ protected double[] attenuationDistance = null;
+ protected float[] attenuationGain = null;;
+
+ /**
+ * dirty flags denoting what has changed since last rendering
+ */
+ protected int dirtyFlags = 0xFFFF;
+
+ /*
+ *
+ * Direction sample fields
+ *
+ */
+ /**
+ * The Cone Sound's direction vector. This is the cone axis.
+ */
+ protected Vector3f direction = new Vector3f(0.0f, 0.0f, 1.0f);
+
+ /**
+ * Pairs of distances and gain scale factors that define piecewise linear
+ * gain BACK attenuation between each pair.
+ * These are used for defining elliptical attenuation regions.
+ */
+ protected double[] backAttenuationDistance = null;
+ protected float[] backAttenuationGain = null;
+
+ /**
+ * Directional Sound's gain can be attenuated based on the listener's
+ * location off-angle from the source source direction.
+ * This can be set by three parameters:
+ * angular distance in radians
+ * gain scale factor
+ * filtering (currently the only filtering supported is lowpass)
+ */
+ protected double[] angularDistance = {0.0, (Math.PI * 0.5)};
+ protected float[] angularGain = {1.0f, 0.0f};
+
+ /**
+ * Distance Filter
+ * Each sound source is attenuated by a filter based on it's distance
+ * from the listener.
+ * For now the only supported filterType will be LOW_PASS frequency
+ * cutoff.
+ * At some time full FIR filtering will be supported.
+ */
+ public static final int NO_FILTERING = -1;
+ public static final int LOW_PASS = 1;
+
+ protected int angularFilterType = NO_FILTERING;
+ protected float[] angularFilterCutoff = {Sound.NO_FILTER, Sound.NO_FILTER};
+
+ /*
+ * Obstruction and Occlusion parameters
+ * For now the only type of filtering supported is a low-pass filter
+ * defined by a frequency cutoff value.
+ * @since Java 3D 1.3
+ */
+ protected float obstructionGain = 1.0f; // scale factor
+ protected int obstructionFilterType = NO_FILTERING;
+ protected float obstructionFilterCutoff = Sound.NO_FILTER;
+ protected float occlusionGain = 1.0f; // scale factor
+ protected int occlusionFilterType = NO_FILTERING;
+ protected float occlusionFilterCutoff = Sound.NO_FILTER;
+
+ /*
+ * Construct a new audio device Sample object
+ */
+ public Sample() {
+ if (debugFlag)
+ debugPrintln("Sample constructor");
+ }
+
+ public long getDuration() {
+ return 0;
+ }
+
+ public long getStartTime() {
+ return 0;
+ }
+
+ public int getNumberOfChannelsUsed() {
+ return 0;
+ }
+
+ public void setDirtyFlags(int flags) {
+ dirtyFlags = flags;
+ }
+
+ public int getDirtyFlags() {
+ return dirtyFlags;
+ }
+
+ public void setSoundType(int type) {
+ soundType = type;
+ }
+
+ public int getSoundType() {
+ return soundType;
+ }
+
+ public void setSoundData(MediaContainer ref) {
+ soundData = ref;
+ }
+
+ public MediaContainer getSoundData() {
+ return soundData;
+ }
+
+ public void setMuteFlag(boolean flag) {
+ mute = flag;
+ }
+
+ public boolean getMuteFlag() {
+ return mute;
+ }
+
+ public void setVWrldXfrmFlag(boolean flag) {
+ // this flag is ONLY true if the VirtualWorld Transform is ever set
+ vwXfrmFlag = flag;
+ }
+
+ public boolean getVWrldXfrmFlag() {
+ return vwXfrmFlag;
+ }
+
+ public void setGain(float scaleFactor) {
+ gain = scaleFactor;
+ }
+
+ public float getGain() {
+ return gain;
+ }
+
+ public void setLoopCount(int count) {
+ loopCount = count;
+ }
+
+ public int getLoopCount() {
+ return loopCount;
+ }
+
+
+ public void setPosition(Point3d position) {
+ this.position.set(position);
+ return;
+ }
+
+ // TODO: no get method for Position
+
+
+ public void setDistanceGain(
+ double[] frontDistance, float[] frontAttenuationScaleFactor,
+ double[] backDistance, float[] backAttenuationScaleFactor) {
+ if (frontDistance != null) {
+ int size = frontDistance.length;
+ attenuationDistance = new double[size];
+ attenuationGain = new float[size];
+ for (int i=0; i<size; i++) {
+ attenuationDistance[i] = frontDistance[i];
+ attenuationGain[i] = frontAttenuationScaleFactor[i];
+ }
+ }
+ else {
+ attenuationDistance = null;
+ attenuationGain = null;
+ }
+ if (backDistance != null && frontDistance != null) {
+ int size = backDistance.length;
+ backAttenuationDistance = new double[size];
+ backAttenuationGain = new float[size];
+ for (int i=0; i<size; i++) {
+ backAttenuationDistance[i] = backDistance[i];
+ backAttenuationGain[i] = backAttenuationScaleFactor[i];
+ }
+ }
+ else {
+ backAttenuationDistance = null;
+ backAttenuationGain = null;
+ }
+ return;
+ }
+
+ // TODO: no get method for Back Attenuation
+
+
+ public void setDirection(Vector3d direction) {
+ this.direction.set(direction);
+ return;
+ }
+
+ // TODO: no get method for Direction
+
+
+ public void setAngularAttenuation(int filterType, double[] angle,
+ float[] attenuationScaleFactor, float[] filterCutoff) {
+ if (angle != null) {
+ int size = angle.length;
+ angularDistance = new double[size];
+ angularGain = new float[size];
+ if (filterType != NO_FILTERING && filterCutoff != null)
+ angularFilterCutoff = new float[size];
+ else
+ angularFilterCutoff = null;
+ for (int i=0; i<size; i++) {
+ angularDistance[i] = angle[i];
+ angularGain[i] = attenuationScaleFactor[i];
+ if (filterType != NO_FILTERING)
+ angularFilterCutoff[i] = filterCutoff[i];
+ }
+ angularFilterType = filterType;
+ }
+ else {
+ angularDistance = null;
+ angularGain = null;
+ angularFilterCutoff = null;
+ angularFilterType = NO_FILTERING;
+ }
+ }
+
+ // TODO: no get method for Angular Attenuation
+
+
+ /*
+ * Set Rate ScaleFactor
+ * @since Java 3D 1.3
+ */
+ public void setRateScaleFactor(float scaleFactor) {
+ rateScaleFactor = scaleFactor;
+ }
+
+ /*
+ * Get Rate ScaleFactor
+ * @since Java 3D 1.3
+ */
+ public float getRateScaleFactor() {
+ return rateScaleFactor;
+ }
+
+
+ /*
+ * Set Obstruction Gain
+ * @since Java 3D 1.3
+ */
+ public void setObstructionGain(float scaleFactor) {
+ obstructionGain = scaleFactor;
+ }
+
+ /*
+ * Get Obstruction Gain
+ * @since Java 3D 1.3
+ */
+ public float getObstructionGain() {
+ return obstructionGain;
+ }
+
+ /*
+ * Set Obstruction Filter Cutoff Frequency
+ * @since Java 3D 1.3
+ */
+ public void setObstructionFilter(float cutoffFrequency) {
+ obstructionFilterType = LOW_PASS;
+ obstructionFilterCutoff = cutoffFrequency;
+ }
+
+ // TODO: no get method for Obstruction Filtering
+
+
+ /*
+ * Set Occlusion Gain
+ * @since Java 3D 1.3
+ */
+ public void setOcclusionGain(float scaleFactor) {
+ occlusionGain = scaleFactor;
+ }
+
+ /*
+ * Get Occlusion Gain
+ * @since Java 3D 1.3
+ */
+ public float getOcclusionGain() {
+ return occlusionGain;
+ }
+
+ /*
+ * Set Occlusion Filter Cutoff Frequency
+ * @since Java 3D 1.3
+ */
+ public void setOcclusionFilter(float cutoffFrequency) {
+ occlusionFilterType = LOW_PASS;
+ occlusionFilterCutoff = cutoffFrequency;
+ }
+
+ // TODO: no get method for Occlusion Filtering
+
+
+ /**
+ * Clears/re-initialize fields associated with sample data
+ * for this sound,
+ * and frees any device specific data associated with this sample.
+ */
+ public void clear() {
+ if (debugFlag)
+ debugPrintln("Sample.clear() entered");
+ soundData = (MediaContainer)null;
+ soundType = NULL_SAMPLE;
+ gain = 1.0f;
+ loopCount = 0;
+ duration = DURATION_UNKNOWN;
+ numberOfChannels = 0;
+ vworldXfrm.setIdentity();
+ vwXfrmFlag = false;
+ position.set(0.0f, 0.0f, 0.0f);
+ attenuationDistance = null;
+ attenuationGain = null;
+ direction.set(0.0f, 0.0f, 1.0f);
+ backAttenuationDistance = null;
+ backAttenuationGain = null;
+ if (angularDistance != null) {
+ angularDistance[0] = 0.0f;
+ angularDistance[1] = (float)(Math.PI) * 0.5f;
+ }
+ if (angularGain != null) {
+ angularGain[0] = 1.0f;
+ angularGain[1] = 0.0f;
+ }
+ angularFilterType = NO_FILTERING;
+ if (angularFilterCutoff != null) {
+ angularFilterCutoff[0] = Sound.NO_FILTER;
+ angularFilterCutoff[1] = Sound.NO_FILTER;
+ }
+ obstructionGain = 1.0f;
+ obstructionFilterType = NO_FILTERING;
+ obstructionFilterCutoff = Sound.NO_FILTER;
+ occlusionGain = 1.0f;
+ occlusionFilterType = NO_FILTERING;
+ occlusionFilterCutoff = Sound.NO_FILTER;
+ }
+
+ /*
+ * Render
+ */
+ public void render(int dirtyFlags, View view, AuralParameters attribs) {
+ // meant to be overridden
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/audioengines/javasound/JSAuralParameters.java b/src/classes/share/com/sun/j3d/audioengines/javasound/JSAuralParameters.java
new file mode 100755
index 0000000..58ef551
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/audioengines/javasound/JSAuralParameters.java
@@ -0,0 +1,79 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.audioengines.javasound;
+
+import java.lang.String;
+import java.io.*;
+
+/**
+ * The AudioDevice dependent sound node and aural attribute node parameters.
+ * These are explicitly maintained for HaeSoundMixer
+ */
+
+public class JSAuralParameters extends com.sun.j3d.audioengines.AuralParameters {
+
+ /**
+ * Reverb Parameters
+ *
+ * dirty flag checked and cleared by render()
+ */
+ static int REFLECTION_COEFF_CHANGED = 1;
+ static int REVERB_DELAY_CHANGED = 2;
+ static int REVERB_ORDER_CHANGED = 4;
+
+ int reverbDirty = 0xFFFF;
+ int lastReverbSpeed = 0; // TODO: NOT used yet
+ boolean reverbFlag = false; // previously refered to as reverbOn
+ int reverbType = 1; // Reverb type 1 equals NONE in JavaSound engine
+
+
+ JSAuralParameters() {
+ super();
+ reverbDirty = 0xFFFF;
+ lastReverbSpeed = 0;
+ reverbType = 1;
+ reverbFlag = false;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/audioengines/javasound/JSChannel.java b/src/classes/share/com/sun/j3d/audioengines/javasound/JSChannel.java
new file mode 100644
index 0000000..4fb4912
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/audioengines/javasound/JSChannel.java
@@ -0,0 +1,435 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+/*
+ * IMPLEMENTATION NOTE: The JavaSoundMixer is incomplete and really needs
+ * to be rewritten.
+ */
+
+package com.sun.j3d.audioengines.javasound;
+
+// import java.applet.*;
+import java.util.*;
+import java.lang.String;
+import java.net.*;
+import java.io.*;
+import java.io.InputStream;
+// import sun.applet.*;
+import javax.sound.sampled.*;
+import com.sun.j3d.audioengines.*;
+// import javax.media.j3d.*;
+
+/**
+ * The JSChannel Class defines an audio output methods that call JavaSound
+ * API methods common for all data line types: streams, clip and MIDI lines.
+ */
+
+class JSChannel {
+
+ AudioInputStream ais = null;
+ long startTime = 0;
+ URL url = null;
+ InputStream inputStream = null;
+ AudioFormat audioFormat = null;
+ // WORKAROUND for (possibly old) bug in JavaSound
+ // JavaSound has left and right flipped
+ // TODO: verify whether this is still true
+ static double panLeft = 1.0;
+ static double panRight = -1.0;
+ float rateInHz = 0.0f;
+
+ /**
+ * Debug print mechanism for Sound nodes
+ */
+ static final boolean debugFlag = false;
+
+ static void debugPrint(String message) {
+ if (debugFlag)
+ System.out.print(message);
+ }
+
+ static void debugPrintln(String message) {
+ if (debugFlag)
+ System.out.println(message);
+ }
+
+
+ /**
+ * Code to initialize the device
+ * @return flag: true is initialized sucessfully, false if error
+ */
+ boolean initialize() {
+ // for now do nothing
+ return true;
+ }
+
+ /**
+ * @return reference to newly created AudioInputStream
+ */
+ AudioInputStream initAudioInputStream(InputStream inputStream, boolean cacheFlag) {
+ ais = null;
+ if (inputStream == null) {
+ if (debugFlag) {
+ debugPrint("JSChannel: Internal Error initAudioInputStream ");
+ debugPrintln("input stream given is null");
+ }
+ this.inputStream = null;
+ return null;
+ }
+ try {
+ if (debugFlag)
+ debugPrintln("JSChannel: initAudioInputStream - try getting stream ");
+ // open the sound data as an 'audio input stream'
+ // and read the header information at the start of the data.
+ ais = AudioSystem.getAudioInputStream(inputStream);
+ // add this new stream to vector list of streams
+ }
+ catch (Exception e) {
+ if (debugFlag) {
+ debugPrint("JSChannel: Internal Error initAudioInputStream ");
+ debugPrintln("get stream failed");
+ }
+ e.printStackTrace();
+ this.inputStream = null;
+ return null;
+ }
+ // success, so save new inputStream and nullify url field
+ this.inputStream = inputStream;
+ url = null;
+/******
+// QUESTION: HOW do I figure out the data type of the file/url/inputStream????
+ if (ais instanceof AudioMidiInputStream ||
+ ais instanceof AudioRmfInputStream )
+ // QUESTION: can non-cached MIDI files ever be supported ?
+*******/
+ return ais;
+ } // initAudioInputStream
+
+
+ /**
+ * @return reference to newly created AudioInputStream
+ */
+ AudioInputStream initAudioInputStream(URL path, boolean cacheFlag) {
+ ais = null;
+ if (path == null) {
+ if (debugFlag) {
+ debugPrint("JSChannel: Internal Error initAudioInputStream ");
+ debugPrintln("URL given is null");
+ }
+ this.url = null;
+ return null;
+ }
+ try {
+ if (debugFlag)
+ debugPrintln("JSChannel: initAudioInputStream - try getting stream ");
+ ais = AudioSystem.getAudioInputStream(path.openStream());
+ }
+ catch (Exception e) {
+ if (debugFlag) {
+ debugPrint("JSChannel: Internal Error initAudioInputStream ");
+ debugPrintln("get stream failed");
+ }
+ e.printStackTrace();
+ this.url = null;
+ return null;
+ }
+ // success, so save new url path and nullify input stream field
+ this.url = path;
+ inputStream = null;
+ return ais;
+ } // initAudioInputStream
+
+
+ AudioInputStream reinitAudioInputStream(URL path) {
+/*****
+ if (path == null) {
+ if (debugFlag) {
+ debugPrint("JSChannel: Internal Error reinitAudioInputStream ");
+ debugPrintln("URL given is null");
+ }
+ return null;
+ }
+ try {
+ if (debugFlag)
+ debugPrintln("JSChannel: reinitAudioInputStream - try getting stream ");
+ ais = AudioSystem.getAudioInputStream(path.openStream());
+ }
+ catch (Exception e) {
+ if (debugFlag) {
+ debugPrint("JSChannel: Internal Error reinitAudioInputStream ");
+ debugPrintln("get stream failed");
+ }
+ e.printStackTrace();
+ return null;
+ }
+ // Parametes stay the same except for start time which is changed later
+ return ais;
+******/
+ return null; // TODO: implement this
+
+ } // reinitAudioInputStream
+
+ AudioInputStream reinitAudioInputStream(InputStream inputStream) {
+/******
+ AudioInputStream ais;
+ if (inputStream == null) {
+ if (debugFlag) {
+ debugPrint("JSChannel: Internal Error reinitAudioInputStream ");
+ debugPrintln("InputStream given is null");
+ }
+ return null;
+ }
+ try {
+// Couldn't get this method to work!!!
+ if (debugFlag)
+ debugPrintln("JSChannel: reintAudioContainer - try closing stream ");
+ inputStream.close();
+
+ if (debugFlag)
+ debugPrintln("JSChannel: reinitAudioInputStream - try getting stream ");
+ ais = AudioSystem.getAudioInputStream(inputStream);
+ }
+ catch (Exception e) {
+ if (debugFlag) {
+ debugPrint("JSChannel: Internal Error reinitAudioInputStream ");
+ debugPrintln("get stream failed");
+ }
+ e.printStackTrace();
+ return null;
+ }
+ // Parametes stay the same except for start time which is changed later
+ return ais; // >=0 if everythings OK
+**************/
+ return null; // TODO: implement this
+
+ } // reinitAudioInputStream
+
+
+ DataLine initDataLine(AudioInputStream ais) {
+ if (debugFlag) {
+ debugPrintln("JSChannel: initDataLine(" + ais + ")");
+ debugPrintln(" must be overridden");
+ }
+ return null;
+ }
+
+ long getDuration() {
+ // TODO: how should this really be done??
+ if (debugFlag)
+ debugPrintln("JSChannel:getDuration");
+
+ if (ais == null || audioFormat == null ) {
+ if (debugFlag)
+ debugPrintln("JSChannel: Internal Error getDuration");
+ return (long)Sample.DURATION_UNKNOWN;
+ }
+ // Otherwise we'll assume that we can calculate this duration
+
+ // get "duration" of audio stream (wave file)
+ // TODO: For True STREAMing audio the size is unknown...
+ long numFrames = ais.getFrameLength();
+ if (debugFlag)
+ debugPrintln(" frame length = " + numFrames);
+ if (numFrames <= 0)
+ return (long)Sample.DURATION_UNKNOWN;
+
+ float rateInFrames = audioFormat.getFrameRate();
+ rateInHz = audioFormat.getSampleRate();
+ if (debugFlag)
+ debugPrintln(" rate in Frames = " + rateInFrames);
+ if (numFrames <= 0)
+ return (long)Sample.DURATION_UNKNOWN;
+ long duration = (long)((float)numFrames/rateInFrames);
+ if (debugFlag)
+ debugPrintln(" duration(based on ais) = " + duration);
+ return duration;
+ }
+
+ /**
+ * Start TWO Samples
+ */
+ boolean startSamples(int loopCount, float leftGain, float rightGain,
+ int leftDelay, int rightDelay) {
+ if (debugFlag)
+ debugPrint("JSChannel: startSamples must be overridden");
+ return false;
+ } // end of start Samples
+
+ /*
+ * Starts a Sample
+ */
+ boolean startSample(int loopCount, float gain, int delay) {
+ if (debugFlag)
+ debugPrint("JSChannel: startSample must be overridden");
+ return false;
+ } // end of start (single) Sample
+
+ int stopSample() {
+// This will tell thread to stop reading and writing
+ // reload with old URL
+ // reloadSample
+ if (debugFlag)
+ debugPrint("JSChannel: stopSample must be overridden");
+ startTime = 0;
+ return 0;
+ }
+
+ int stopSamples() {
+// This will tell thread to stop reading and writing
+ // TODO: For muting, stop sound but don't clear startTime...
+ // QUESTION: what does it mean for replaying that .stop "frees memory"
+ if (debugFlag)
+ debugPrint("JSChannel: stopSample must be overridden");
+// reloadSample
+
+ startTime = 0;
+ return 0;
+ }
+
+ void setSampleGain(float gain) {
+// TODO: Must be done in thread
+ if (debugFlag)
+ debugPrint("JSChannel: setSampleGain must be overridden");
+ }
+
+ void setSampleDelay(int delay) {
+ if (debugFlag)
+ debugPrint("JSChannel: setSampleDelay must be overridden");
+ /*
+ * null method
+ */
+ // dynamic changes to sample delay while playing is not implemented
+ }
+
+ void setSampleReverb(int type, boolean on) {
+ if (debugFlag)
+ debugPrint("JSChannel: setSampleReverb must be overridden");
+ }
+
+ void setSampleRate() {
+ if (debugFlag)
+ debugPrint("JSChannel: setSampleRate must be overridden");
+ }
+ void scaleSampleRate(float scaleFactor) {
+ /**
+ * Change rate for Doppler affect or pitch shifting.
+ * Engine maximum sample rate is 48kHz so clamp to that
+ * max value.
+ */
+ if (debugFlag)
+ debugPrintln("JSChannel: scaleSampleRate");
+ if (ais == null) {
+ if (debugFlag) {
+ debugPrint("JSChannel: Internal Error scaleSampleRate: ");
+ debugPrintln("ais is null");
+ }
+ return;
+ }
+
+ AudioFormat audioFormat = ais.getFormat();
+ float rate = audioFormat.getSampleRate();
+
+ double newRate = rate * scaleFactor;
+ if (newRate > 48000.0) // clamp to 48K max
+ newRate = 48000.0;
+/****
+// NOTE: This doesn't work...
+/// audioStream.setSampleRate(newRate);
+
+// need to set FloatControl.Type(SAMPLE_RATE) to new value somehow...
+
+ if (debugFlag) {
+ debugPrintln("JSChannel: scaleSampleRate: new rate = " +
+ rate * scaleFactor);
+ debugPrintln(" >>>>>>>>>>>>>>> using scaleFactor = " +
+ scaleFactor);
+ }
+****/
+ }
+
+ int pauseSamples() {
+ /**
+ * Pause playing samples
+ */
+// TODO: Notify thread
+ return 0;
+ }
+
+ int pauseSample() {
+ /**
+ * Pause playing a sample
+ */
+// TODO: Notify thread
+ return 0;
+ }
+
+ int unpauseSamples() {
+ /**
+ * Resume playing samples
+ */
+// TODO: Notify thread
+ return 0;
+ }
+
+ int unpauseSample() {
+ /**
+ * Resume playing a sample
+ */
+// TODO: Notify thread
+ return 0;
+ }
+
+ void setSampleFiltering(boolean filterFlag, float cutoffFreq) {
+ /**
+ * Set or clear low-pass filtering
+ */
+/****
+// QUESTION: how will this be done if data is written out one channel/sample at
+ a time??
+****/
+ // QUESTION: should filtering of Midi data be performed?
+// ais.setFiltering(filterFlag, cutoffFreq);
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/audioengines/javasound/JSClip.java b/src/classes/share/com/sun/j3d/audioengines/javasound/JSClip.java
new file mode 100755
index 0000000..a1558fa
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/audioengines/javasound/JSClip.java
@@ -0,0 +1,349 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+/*
+ * IMPLEMENTATION NOTE: The JavaSoundMixer is incomplete and really needs
+ * to be rewritten.
+ */
+
+package com.sun.j3d.audioengines.javasound;
+
+import java.applet.*;
+import java.util.*;
+import java.lang.String;
+import java.net.*;
+import java.io.*;
+import java.io.InputStream;
+import javax.sound.sampled.*;
+
+/**
+ * The JSClip Class defines an audio output methods that call JavaSound
+ * Hae mixer methods.
+ */
+
+class JSClip extends JSChannel {
+
+ Clip line;
+
+// TODO: separate left and right channel required until I write into
+// stereo buffer!
+ Clip otherChannel = null;
+
+// TODO: Reverb channel that is centered and not delayed is maintained separately
+// until a way to set stereo reverb send (panned and attenuated to give
+// the same affect) is implemented
+ Clip reverbChannel = null;
+
+
+ /**
+ * Create data line for outputting audio input stream.
+ * for a stream that is a sourceDataline
+ * @return true is successful in initiallizing DataLine
+ */
+ DataLine initDataLine(AudioInputStream ais) {
+ if (debugFlag)
+ debugPrintln("JSClip: initDataLine(" + ais + ")");
+
+ try {
+ if (debugFlag)
+ debugPrintln("JSClip: loadSample - try getting new line ");
+ /*
+ * From the AudioInputStream fetch information about the format
+ * of the audio data - including sampling frequency, number of
+ * channels, size of samples,...
+ */
+ audioFormat = ais.getFormat();
+
+ /*
+ * we can't yet open the device for ALAW/ULAW playback,
+ * convert ALAW/ULAW to PCM
+ */
+ if ((audioFormat.getEncoding() == AudioFormat.Encoding.ULAW) ||
+ (audioFormat.getEncoding() == AudioFormat.Encoding.ALAW)) {
+
+ AudioFormat tmp =
+ new AudioFormat(
+ AudioFormat.Encoding.PCM_SIGNED,
+ audioFormat.getSampleRate(),
+ audioFormat.getSampleSizeInBits() * 2,
+ audioFormat.getChannels(),
+ audioFormat.getFrameSize() * 2,
+ audioFormat.getFrameRate(),
+ true);
+ ais = AudioSystem.getAudioInputStream(tmp, ais);
+ audioFormat = tmp;
+ }
+
+ /*
+ * ask JavaSound for outline with a format suitable for our
+ * AudioInputStream. In order to ask for a line, a Info object
+ * with the desired properties must be constructed.
+ * Clip is used for outputing buffered data.
+ * We have to pass the line the AudioFormat object so it knows
+ * format will be.
+ *
+ * TODO: we could give JavaSound a hint about how big the
+ * internal buffer for the line should be, rather than use the
+ * default.
+ */
+ DataLine.Info info = new DataLine.Info(Clip.class,
+ audioFormat);
+ line = (Clip)AudioSystem.getLine(info);
+/*****
+// TODO: JSClip can't be a listener (do we need to do this in the thread?)
+ if (debugFlag)
+ debugPrintln("JSClip: addLineListener for clip");
+ line.addLineListener(this);
+******/
+
+ if (debugFlag)
+ debugPrintln("JSClip: open sound Clip");
+
+ // Make line ready to receive data.
+ line.open(ais);
+
+ // Line can now receive data but still needs to be
+ // activated (opened) so it will pass data on to the
+ // audio device. This is done at "startSample" time.
+ }
+ catch (Exception e) {
+ if (debugFlag) {
+ debugPrint("JSClip: Internal Error loadSample ");
+ debugPrintln("get stream failed");
+ }
+ e.printStackTrace();
+ // TODO: clean up vector elements that were set up for
+ // failed sample
+ return null;
+ }
+ return (DataLine)line;
+ } // initDataLine
+
+ /**
+ * Start TWO Samples
+ *
+ * used when two samples are associated with a single Point or Cone
+ * sound. This method handles starting both samples, rather than
+ * forcing the caller to make two calls to startSample, so that the
+ * actual Java Sound start methods called are as immediate (without
+ * delay between as possible.
+ */
+ boolean startSamples(int loopCount, float leftGain, float rightGain,
+ int leftDelay, int rightDelay) {
+ // loop count is ignored for Stream and MIDI
+ // TODO: loop count isn't implemented for MIDI yet
+
+ // left and rightDelay parameters are in terms of Samples
+ if (debugFlag) {
+ debugPrint("JSClip: startSamples ");
+ debugPrintln("start stream for Left called with ");
+ debugPrintln(" gain = " + leftGain +
+ " delay = " + leftDelay);
+ debugPrintln("start stream for Right called with ");
+ debugPrintln(" gain = " + rightGain +
+ " delay = " + rightDelay);
+ }
+
+ // This is called assuming that the Stream is allocated for a
+ // Positional sample, but if it is not then fall back to
+ // starting the single sample associated with this Stream
+ if (otherChannel == null || reverbChannel == null)
+ startSample(loopCount, leftGain, leftDelay);
+
+ /*
+ * ais for Left and Right streams should be same so just get ais
+ * left stream
+ */
+ if (ais == null) {
+ if (debugFlag) {
+ debugPrint("JSClip: Internal Error startSamples: ");
+ debugPrintln("either left or right ais is null");
+ }
+ return false;
+ }
+ Clip leftLine;
+ Clip rightLine;
+ leftLine = line;
+ rightLine = otherChannel;
+// left line only for background sounds...
+// TODO:
+/***********
+for now just care about the left
+ if (leftLine == null || rightLine == null) {
+ if (debugFlag) {
+ debugPrint("JSClip: startSamples Internal Error: ");
+ debugPrintln("either left or right line null");
+ }
+ return false;
+ }
+************/
+
+ // we know that were processing TWO channels
+ double ZERO_EPS = 0.0039; // approx 1/256 - twice MIDI precision
+ double leftVolume = (double)leftGain;
+ double rightVolume = (double)rightGain;
+
+// TODO: if not reading/writing done for Clips then I can't do
+// stereo trick (reading mono file and write to stereo buffer)
+ // Save time sound started, only in left
+ startTime = System.currentTimeMillis();
+ if (debugFlag)
+ debugPrintln("*****start Stream with new start time " +
+ startTime);
+ try {
+ // QUESTION: Offset clip is done how???
+/*******
+// TODO:
+offset delayed sound
+set volume
+set pan??
+set reverb
+ boolean reverbLeft = false; // off; reverb has it own channel
+ boolean reverbRight = reverbLeft;
+
+ if (leftDelay < rightDelay) {
+XXXX audioLeftStream.start(leftVolume, panLeft, reverbLeft);
+XXXX audioRightStream.start(rightVolume, panRight, reverbRight);
+ }
+ else {
+XXXX audioRightStream.start(rightVolume, panRight, reverbRight);
+XXXX audioLeftStream.start(leftVolume, panLeft, reverbLeft);
+ }
+******/
+ line.setLoopPoints(0, -1); // Loop the entire sound sample
+ line.loop(loopCount); // plays clip loopCount + 1 times
+ line.start(); // start the sound
+ }
+ catch (Exception e) {
+ if (debugFlag) {
+ debugPrint("JSClip: startSamples ");
+ debugPrintln("audioInputStream.read failed");
+ }
+ e.printStackTrace();
+ startTime = 0;
+ return false;
+ }
+
+ if (debugFlag)
+ debugPrintln("JSClip: startSamples returns");
+ return true;
+ } // end of startSamples
+
+
+ /*
+ * This method is called specifically for BackgroundSounds.
+ * There is exactly ONE sample (mono or stereo) associated with
+ * this type of sound. Consequently, delay is not applicable.
+ * Since the sound has no auralAttributes applied to it reverb
+ * is not applied to the sample.
+ */
+ boolean startSample(int loopCount, float gain, int delay) {
+ /*
+ if (debugFlag) {
+ debugPrint("JSClip: startSample ");
+ debugPrintln("start stream called with ");
+ debugPrintln(" gain = " + gain + ", delay is zero");
+ }
+
+ // Since only one sample is processed in startSample, just call
+ // this more general method passing duplicate information
+ // We don't really want to do this in the long term.
+ return startSamples(loopCount, gain, gain, 0, 0);
+ */
+
+ // TODO: The following is temporary until we have fully
+ // functional startSample and startSamples methods
+ if (debugFlag)
+ debugPrintln("JSClip.startSample(): starting sound Clip");
+ line.setFramePosition(0); // Start playing from the beginning
+ line.setLoopPoints(0, -1); // Loop the entire sound sample
+ line.loop(loopCount);
+ line.start();
+ return true;
+ } // end of start (single) Sample
+
+ int stopSample() {
+ // This will tell thread to stop reading and writing
+ // reload with old URL - reloadSample()???
+
+ if (debugFlag)
+ debugPrintln("JSClip.stopSample(): stopping sound Clip");
+ line.stop();
+
+ startTime = 0;
+ return 0;
+ }
+
+ int stopSamples() {
+ // This will tell thread to stop reading and writing
+ // TODO: For muting, stop sound but don't clear startTime...
+ // QUESTION: what does it mean for replaying that .stop "frees memory"
+
+ // reloadSample
+ // QUESTION: set stop state WHERE??!!
+
+ if (debugFlag)
+ debugPrintln("JSClip.stopSample(): stopping sound Clip");
+ line.stop();
+
+ startTime = 0;
+ return 0;
+ }
+
+ /*
+ * called by LineListener class
+ */
+ public void update(LineEvent event) {
+ if (event.getType().equals(LineEvent.Type.STOP)) {
+ line.close(); // really a stop??
+ }
+ else if (event.getType().equals(LineEvent.Type.CLOSE)) {
+ // this forces a system exit in example code
+ // TODO: what should be done to close line
+ if (debugFlag)
+ debugPrint("JSClip.update(CLOSE) entered ");
+ }
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/audioengines/javasound/JSDirectionalSample.java b/src/classes/share/com/sun/j3d/audioengines/javasound/JSDirectionalSample.java
new file mode 100755
index 0000000..ce8a97f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/audioengines/javasound/JSDirectionalSample.java
@@ -0,0 +1,738 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+/*
+ * DirectionalSample object
+ *
+ * IMPLEMENTATION NOTE: The JavaSoundMixer is incomplete and really needs
+ * to be rewritten.
+ */
+
+package com.sun.j3d.audioengines.javasound;
+
+import javax.media.j3d.*;
+import com.sun.j3d.audioengines.*;
+import javax.vecmath.*;
+
+/**
+ * The PostionalSample Class defines the data and methods associated with a
+ * PointSound sample played through the AudioDevice.
+ */
+
+class JSDirectionalSample extends JSPositionalSample
+{
+ // The transformed direction of this sound
+ Vector3f xformDirection = new Vector3f(0.0f, 0.0f, 1.0f);
+
+ public JSDirectionalSample() {
+ super();
+ if (debugFlag)
+ debugPrintln("JSDirectionalSample constructor");
+ }
+
+ void setXformedDirection() {
+ if (debugFlag)
+ debugPrint("*** setXformedDirection");
+ if (!getVWrldXfrmFlag()) {
+ if (debugFlag)
+ debugPrint(" Transform NOT set yet, so dir => xformDir");
+ xformDirection.set(direction);
+ }
+ else {
+ if (debugFlag)
+ debugPrint(" Transform dir => xformDir");
+ vworldXfrm.transform(direction, xformDirection);
+ }
+ if (debugFlag)
+ debugPrint(" xform(sound)Direction <= "+xformDirection.x+
+ ", " + xformDirection.y + ", " + xformDirection.z);
+ }
+
+
+ /* ***********************************
+ *
+ * Intersect ray to head with Ellipse
+ *
+ * ***********************************/
+ /*
+ * An ellipse is defined using:
+ * (1) the ConeSound's direction vector as the major axis of the ellipse;
+ * (2) the max parameter (a front distance attenuation value) along the
+ * cone's position axis; and
+ * (3) the min parameter (a back distance attenuation value) along the
+ * cone's negative axis
+ * This method calculates the distance from the sound source to the
+ * Intersection of the Ellipse with the ray from the sound source to the
+ * listener's head.
+ * This method returns the resulting distance.
+ * If an error occurs, -1.0 is returned.
+ *
+ * A calculation are done in 'Cone' space:
+ * The origin is defined as being the sound source position.
+ * The ConeSound source axis is the X-axis of this Cone's space.
+ * Since this ConeSound source defines a prolate spheroid (obtained
+ * by revolving an ellipsoid about the major axis) we can define the
+ * Y-axis of this Cone space as being in the same plane as the X-axis
+ * and the vector from the origin to the head.
+ * All calculations in Cone space can be generalized in this two-
+ * dimensional space without loss of precision.
+ * Location of the head, H, in Cone space can then be defined as:
+ * H'(x,y) = (cos @, sin @) * | H |
+ * where @ is the angle between the X-axis and the ray to H.
+ * Using the equation of the line thru the origin and H', and the
+ * equation of ellipse defined with min and max, find the
+ * intersection by solving for x and then y.
+ *
+ * (I) The equation of the line thru the origin and H', and the
+ * | H'(y) - S(y) |
+ * y - S(y) = | ----------- | * [x - S(x)]
+ * | H'(x) - S(x) |
+ * and since S(x,y) is the origin of ConeSpace:
+ * | H'(y) |
+ * y = | ----- | x
+ * | H'(x) |
+ *
+ * (II) The equation of ellipse:
+ * x**2 y**2
+ * ---- + ---- = 1
+ * a**2 b**2
+ * given a is length from origin to ellipse along major, X-axis, and
+ * b is length from origin to ellipse along minor, Y-axis;
+ * where a**2 = [(max+min)/2]**2 , since 2a = min+max;
+ * where b**2 = min*max , since the triangle abc is made is defined by the
+ * the points: S(x,y), origin, and (0,b),
+ * thus b**2 = a**2 - S(x,y) = a**2 - ((a-min)**2) = 2a*min - min**2
+ * b**2 = ((min+max)*min) - min**2 = min*max.
+ * so the equation of the ellipse becomes:
+ * x**2 y**2
+ * ---------------- + ------- = 1
+ * [(max+min)/2]**2 min*max
+ *
+ * Substuting for y from Eq.(I) into Eq.(II) gives
+ * x**2 [(H'(y)/H'(x))*x]**2
+ * ---------------- + -------------------- = 1
+ * [(max+min)/2]**2 min*max
+ *
+ * issolating x**2 gives
+ * | 1 [H'(y)/H'(x)]**2 |
+ * x**2 | ---------------- + ---------------- | = 1
+ * | [(max+min)/2]**2 min*max |
+ *
+ *
+ * | 4 [(sin @ * |H|)/(cos @ * |H|)]**2 |
+ * x**2 | -------------- + -------------------------------- | = 1
+ * | [(max+min)]**2 min*max |
+ *
+ * | |
+ * | 1 |
+ * | |
+ * x**2 = | --------------------------------------- |
+ * | | 4 [sin @/cos @]**2 | |
+ * | | -------------- + ---------------- | |
+ * | | [(max+min)]**2 min*max | |
+ *
+ * substitute tan @ for [sin @/cos @], and take the square root and you have
+ * the equation for x as calculated below.
+ *
+ * Then solve for y by plugging x into Eq.(I).
+ *
+ * Return the distance from the origin in Cone space to this intersection
+ * point: square_root(x**2 + y**2).
+ *
+ */
+ double intersectEllipse(double max, double min ) {
+
+ if (debugFlag)
+ debugPrint(" intersectEllipse entered with min/max = " + min + "/" + max);
+ /*
+ * First find angle '@' between the X-axis ('A') and the ray to Head ('H').
+ * In local coordinates, use Dot Product of those two vectors to get cos @:
+ * A(u)*H(u) + A(v)*H(v) + A(w)*H(v)
+ * cos @ = --------------------------------
+ * |A|*|H|
+ * then since domain of @ is { 0 <= @ <= PI }, arccos can be used to get @.
+ */
+ Vector3f xAxis = this.direction; // axis is sound direction vector
+ // Get the already calculated vector from sound source position to head
+ Vector3f sourceToHead = this.sourceToCenterEar;
+ // error check vectors not empty
+ if (xAxis == null || sourceToHead == null) {
+ if (debugFlag)
+ debugPrint( " one or both of the vectors are null" );
+ return (-1.0f); // denotes an error occurred
+ }
+
+ // Dot Product
+ double dotProduct = (double)( (sourceToHead.dot(xAxis)) /
+ (sourceToHead.length() * xAxis.length()));
+ if (debugFlag)
+ debugPrint( " dot product = " + dotProduct );
+ // since theta angle is in the range between 0 and PI, arccos can be used
+ double theta = (float)(Math.acos(dotProduct));
+ if (debugFlag)
+ debugPrint( " theta = " + theta );
+
+ /*
+ * Solve for X using Eq.s (I) and (II) from above.
+ */
+ double minPlusMax = (double)(min + max);
+ double tangent = Math.tan(theta);
+ double xSquared = 1.0 /
+ ( ( 4.0 / (minPlusMax * minPlusMax) ) +
+ ( (tangent * tangent) / (min * max) ) );
+ double x = Math.sqrt(xSquared);
+ if (debugFlag)
+ debugPrint( " X = " + x );
+ /*
+ * Solve for y, given the result for x:
+ * | H'(y) | | sin @ |
+ * y = | ----- | x = | ----- | x
+ * | H'(x) | | cos @ |
+ */
+ double y = tangent * x;
+ if (debugFlag)
+ debugPrint( " Y = " + y );
+ double ySquared = y * y;
+
+ /*
+ * Now return distance from origin to intersection point (x,y)
+ */
+ float distance = (float)(Math.sqrt(xSquared + ySquared));
+ if (debugFlag)
+ debugPrint( " distance to intersection = " + distance );
+ return (distance);
+ }
+
+ /* *****************
+ *
+ * Find Factor
+ *
+ * *****************/
+ /*
+ * Interpolates the correct attenuation scale factor given a 'distance'
+ * value. This version used both front and back attenuation distance
+ * and scale factor arrays (if non-null) in its calculation of the
+ * the distance attenuation.
+ * If the back attenuation arrays are null then this executes the
+ * PointSoundRetained version of this method.
+ * This method finds the intesection of the ray from the sound source
+ * to the center-ear, with the ellipses defined by the two sets (front
+ * and back) of distance attenuation arrays.
+ * This method looks at pairs of intersection distance values to find
+ * which pair the input distance argument is between:
+ * [intersectionDistance[index] and intersectionDistance[index+1]
+ * The index is used to get factorArray[index] and factorArray[index+1].
+ * Then the ratio of the 'distance' between this pair of intersection
+ * values is used to scale the two found factorArray values proportionally.
+ */
+ float findFactor(double distanceToHead,
+ double[] maxDistanceArray, float[] maxFactorArray,
+ double[] minDistanceArray, float[] minFactorArray) {
+ int index, lowIndex, highIndex, indexMid;
+ double returnValue;
+
+ if (debugFlag) {
+ debugPrint("JSDirectionalSample.findFactor entered:");
+ debugPrint(" distance to head = " + distanceToHead);
+ }
+
+ if (minDistanceArray == null || minFactorArray == null) {
+ /*
+ * Execute the PointSoundRetained version of this method.
+ * Assume it will check for other error conditions.
+ */
+ return ( this.findFactor(distanceToHead,
+ maxDistanceArray, maxFactorArray) );
+ }
+
+ /*
+ * Error checking
+ */
+ if (maxDistanceArray == null || maxFactorArray == null) {
+ if (debugFlag)
+ debugPrint(" findFactor: arrays null");
+ return -1.0f;
+ }
+ // Assuming length > 1 already tested in set attenuation arrays methods
+ int arrayLength = maxDistanceArray.length;
+ if (arrayLength < 2) {
+ if (debugFlag)
+ debugPrint(" findFactor: arrays length < 2");
+ return -1.0f;
+ }
+ int largestIndex = arrayLength - 1;
+ /*
+ * Calculate distanceGain scale factor
+ */
+ /*
+ * distanceToHead is larger than greatest distance in maxDistanceArray
+ * so head is beyond the outer-most ellipse.
+ */
+ if (distanceToHead >= maxDistanceArray[largestIndex]) {
+ if (debugFlag)
+ debugPrint(" findFactor: distance > " +
+ maxDistanceArray[largestIndex]);
+ if (debugFlag)
+ debugPrint(" maxDistanceArray length = " +
+ maxDistanceArray.length);
+ if (debugFlag)
+ debugPrint(" findFactor returns ****** " +
+ maxFactorArray[largestIndex] + " ******");
+ return maxFactorArray[largestIndex];
+ }
+
+ /*
+ * distanceToHead is smaller than least distance in minDistanceArray
+ * so head is inside the inner-most ellipse.
+ */
+ if (distanceToHead <= minDistanceArray[0]) {
+ if (debugFlag)
+ debugPrint(" findFactor: distance < " +
+ maxDistanceArray[0]);
+ if (debugFlag)
+ debugPrint(" findFactor returns ****** " +
+ minFactorArray[0] + " ******");
+ return minFactorArray[0];
+ }
+
+ /*
+ * distanceToHead is between points within attenuation arrays.
+ * Use binary halfing of distance attenuation arrays.
+ */
+ {
+ double[] distanceArray = new double[arrayLength];
+ float[] factorArray = new float[arrayLength];
+ boolean[] intersectionCalculated = new boolean[arrayLength];
+ // initialize intersection calculated array flags to false
+ for (int i=0; i<arrayLength; i++)
+ intersectionCalculated[i] = false;
+ boolean intersectionOnEllipse = false;
+ int factorIndex = -1;
+
+ /*
+ * Using binary halving to find the two index values in the
+ * front and back distance arrays that the distanceToHead
+ * parameter (from sound source position to head) fails between.
+ * Changing the the current low and high index values
+ * calculate the intesection of ellipses (defined by this
+ * min/max distance values) with the ray (sound source to
+ * head). Put the resulting value into the distanceArray.
+ */
+ /*
+ * initialize the lowIndex to first index of distance arrays.
+ * initialize the highIndex to last index of distance arrays.
+ */
+ lowIndex = 0;
+ highIndex = largestIndex;
+
+ if (debugFlag)
+ debugPrint(" while loop to find index that's closest: ");
+ while (lowIndex < (highIndex-1)) {
+ if (debugFlag)
+ debugPrint(" lowIndex " + lowIndex +
+ ", highIndex " + highIndex);
+ /*
+ * Calculate the Intersection of Ellipses (defined by this
+ * min/max values) with the ray from the sound source to the
+ * head. Put the resulting value into the distanceArray.
+ */
+ if (!intersectionCalculated[lowIndex]) {
+ distanceArray[lowIndex] = this.intersectEllipse(
+ maxDistanceArray[lowIndex], minDistanceArray[lowIndex]);
+ // If return intersection distance is < 0 an error occurred.
+ if (distanceArray[lowIndex] >= 0.0)
+ intersectionCalculated[lowIndex] = true;
+ else {
+ /*
+ * Error in ellipse intersection calculation. Use
+ * average of max/min difference for intersection value.
+ */
+ distanceArray[lowIndex] = (minDistanceArray[lowIndex] +
+ maxDistanceArray[lowIndex])*0.5;
+ if (internalErrors)
+ debugPrint(
+ "Internal Error in intersectEllipse; use " +
+ distanceArray[lowIndex] +
+ " for intersection value " );
+ // Rather than aborting, just use average and go on...
+ intersectionCalculated[lowIndex] = true;
+ }
+ } // end of if intersection w/ lowIndex not already calculated
+
+ if (!intersectionCalculated[highIndex]) {
+ distanceArray[highIndex] = this.intersectEllipse(
+ maxDistanceArray[highIndex],minDistanceArray[highIndex]);
+ // If return intersection distance is < 0 an error occurred.
+ if (distanceArray[highIndex] >= 0.0f)
+ intersectionCalculated[highIndex] = true;
+ else {
+ /*
+ * Error in ellipse intersection calculation. Use
+ * average of max/min difference for intersection value.
+ */
+ distanceArray[highIndex] = (minDistanceArray[highIndex]+
+ maxDistanceArray[highIndex])*0.5f;
+ if (internalErrors)
+ debugPrint(
+ "Internal Error in intersectEllipse; use " +
+ distanceArray[highIndex] +
+ " for intersection value " );
+ // Rather than aborting, just use average and go on...
+ intersectionCalculated[highIndex] = true;
+ }
+ } // end of if intersection w/ highIndex not already calculated
+
+ /*
+ * Test for intersection points being the same as head position
+ * distanceArray[lowIndex] and distanceArray[highIndex], if so
+ * return factor value directly from array
+ */
+ if (distanceArray[lowIndex] >= distanceToHead) {
+ if ((lowIndex != 0) &&
+ (distanceToHead < distanceArray[lowIndex])) {
+ if (internalErrors)
+ debugPrint(
+ "Internal Error: binary halving in " +
+ "findFactor failed; distance < low " +
+ "index value");
+ }
+ if (debugFlag) {
+ debugPrint(" distanceArray[lowIndex] >= " +
+ "distanceToHead" );
+ debugPrint( " factorIndex = " + lowIndex);
+ }
+ intersectionOnEllipse = true;
+ factorIndex = lowIndex;
+ break;
+ }
+ else if (distanceArray[highIndex] <= distanceToHead) {
+ if ((highIndex != largestIndex) &&
+ (distanceToHead > distanceArray[highIndex])) {
+ if (internalErrors)
+ debugPrint(
+ "Internal Error: binary halving in " +
+ "findFactor failed; distance > high " +
+ "index value");
+ }
+ if (debugFlag) {
+ debugPrint(" distanceArray[highIndex] >= " +
+ "distanceToHead" );
+ debugPrint( " factorIndex = " + highIndex);
+ }
+ intersectionOnEllipse = true;
+ factorIndex = highIndex;
+ break;
+ }
+
+ if (distanceToHead > distanceArray[lowIndex] &&
+ distanceToHead < distanceArray[highIndex] ) {
+ indexMid = lowIndex + ((highIndex - lowIndex) / 2);
+ if (distanceToHead <= distanceArray[indexMid])
+ // value of distance in lower "half" of list
+ highIndex = indexMid;
+ else // value if distance in upper "half" of list
+ lowIndex = indexMid;
+ }
+ } /* of while */
+
+ /*
+ * First check to see if distanceToHead is beyond min or max
+ * ellipses, or on an ellipse.
+ * If so, factor is calculated using the distance Ratio
+ * (distanceToHead - min) / (max-min)
+ * where max = maxDistanceArray[factorIndex], and
+ * min = minDistanceArray[factorIndex]
+ */
+ if (intersectionOnEllipse && factorIndex >= 0) {
+ if (debugFlag) {
+ debugPrint( " ratio calculated using factorIndex " +
+ factorIndex);
+ debugPrint( " d.A. max pair for factorIndex " +
+ maxDistanceArray[factorIndex] + ", " +
+ maxFactorArray[factorIndex]);
+ debugPrint( " d.A. min pair for lowIndex " +
+ minDistanceArray[factorIndex] + ", " +
+ minFactorArray[factorIndex]);
+ }
+ returnValue = (
+ ( (distanceArray[factorIndex] -
+ minDistanceArray[factorIndex]) /
+ (maxDistanceArray[factorIndex] -
+ minDistanceArray[factorIndex]) ) *
+ (maxFactorArray[factorIndex] -
+ minFactorArray[factorIndex]) ) +
+ minFactorArray[factorIndex] ;
+ if (debugFlag)
+ debugPrint(" findFactor returns ****** " +
+ returnValue + " ******");
+ return (float)returnValue;
+ }
+
+ /* Otherwise, for distanceToHead between distance intersection
+ * values, we need to calculate two factors - one for the
+ * ellipse defined by lowIndex min/max factor arrays, and
+ * the other by highIndex min/max factor arrays. Then the
+ * distance Ratio (defined above) is applied, using these
+ * two factor values, to get the final return value.
+ */
+ double highFactorValue = 1.0;
+ double lowFactorValue = 0.0;
+ highFactorValue =
+ ( ((distanceArray[highIndex] - minDistanceArray[highIndex]) /
+ (maxDistanceArray[highIndex]-minDistanceArray[highIndex])) *
+ (maxFactorArray[highIndex] - minFactorArray[highIndex]) ) +
+ minFactorArray[highIndex] ;
+ if (debugFlag) {
+ debugPrint( " highFactorValue calculated w/ highIndex " +
+ highIndex);
+ debugPrint( " d.A. max pair for highIndex " +
+ maxDistanceArray[highIndex] + ", " +
+ maxFactorArray[highIndex]);
+ debugPrint( " d.A. min pair for lowIndex " +
+ minDistanceArray[highIndex] + ", " +
+ minFactorArray[highIndex]);
+ debugPrint( " highFactorValue " + highFactorValue);
+ }
+ lowFactorValue =
+ ( ((distanceArray[lowIndex] - minDistanceArray[lowIndex]) /
+ (maxDistanceArray[lowIndex] - minDistanceArray[lowIndex])) *
+ (maxFactorArray[lowIndex] - minFactorArray[lowIndex]) ) +
+ minFactorArray[lowIndex] ;
+ if (debugFlag) {
+ debugPrint( " lowFactorValue calculated w/ lowIndex " +
+ lowIndex);
+ debugPrint( " d.A. max pair for lowIndex " +
+ maxDistanceArray[lowIndex] + ", " +
+ maxFactorArray[lowIndex]);
+ debugPrint( " d.A. min pair for lowIndex " +
+ minDistanceArray[lowIndex] + ", " +
+ minFactorArray[lowIndex]);
+ debugPrint( " lowFactorValue " + lowFactorValue);
+ }
+ /*
+ * calculate gain scale factor based on the ratio distance
+ * between ellipses the distanceToHead lies between.
+ */
+ /*
+ * ratio: distance from listener to sound source
+ * between lowIndex and highIndex times
+ * attenuation value between lowIndex and highIndex
+ * gives linearly interpolationed attenuation value
+ */
+ if (debugFlag) {
+ debugPrint( " ratio calculated using distanceArray" +
+ lowIndex + ", highIndex " + highIndex);
+ debugPrint( " calculated pair for lowIndex " +
+ distanceArray[lowIndex]+", "+ lowFactorValue);
+ debugPrint( " calculated pair for highIndex " +
+ distanceArray[highIndex]+", "+ highFactorValue );
+ }
+
+ returnValue =
+ ( ( (distanceToHead - distanceArray[lowIndex]) /
+ (distanceArray[highIndex] - distanceArray[lowIndex]) ) *
+ (highFactorValue - lowFactorValue) ) +
+ factorArray[lowIndex] ;
+ if (debugFlag)
+ debugPrint(" findFactor returns ******" +
+ returnValue + " ******");
+ return (float)returnValue;
+ }
+
+ }
+
+ /**
+ * CalculateDistanceAttenuation
+ *
+ * Simply calls ConeSound specific 'findFactor()' with
+ * both front and back attenuation linear distance and gain scale factor
+ * arrays.
+ */
+ float calculateDistanceAttenuation(float distance) {
+ float factor = findFactor(distance, this.attenuationDistance,
+ this.attenuationGain, this.backAttenuationDistance,
+ this.backAttenuationGain);
+ if (factor < 0.0f)
+ return 1.0f;
+ else
+ return factor;
+ }
+ /**
+ * CalculateAngularGain
+ *
+ * Simply calls generic (for PointSound) 'findFactor()' with
+ * a single set of angular attenuation distance and gain scalefactor arrays.
+ */
+ float calculateAngularGain() {
+ float angle = findAngularOffset();
+ float factor = findFactor(angle, this.angularDistance, this.angularGain);
+ if (factor < 0.0f)
+ return 1.0f;
+ else
+ return factor;
+ }
+
+ /* *****************
+ *
+ * Find Angular Offset
+ *
+ * *****************/
+ /*
+ * Calculates the angle from the sound's direction axis and the ray from
+ * the sound origin to the listener'center ear.
+ * For Cone Sounds this value is the arc cosine of dot-product between
+ * the sound direction vector and the vector (sound position,centerEar)
+ * all in Virtual World coordinates space.
+ * Center ear position is in Virtual World coordinates.
+ * Assumes that calculation done in VWorld Space...
+ * Assumes that xformPosition is already calculated...
+ */
+ float findAngularOffset() {
+ Vector3f unitToEar = new Vector3f();
+ Vector3f unitDirection = new Vector3f();
+ Point3f xformPosition = positions[currentIndex];
+ Point3f xformCenterEar = centerEars[currentIndex];
+ float dotProduct;
+ float angle;
+ /*
+ * TODO: (Question) is assumption that xformed values available O.K.
+ * TODO: (Performance) save this angular offset and only recalculate
+ * if centerEar or sound position have changed.
+ */
+ unitToEar.x = xformCenterEar.x - xformPosition.x;
+ unitToEar.y = xformCenterEar.y - xformPosition.y;
+ unitToEar.z = xformCenterEar.z - xformPosition.z;
+ unitToEar.normalize();
+ unitDirection.normalize(this.direction);
+ dotProduct = unitToEar.dot(unitDirection);
+ angle = (float)(Math.acos((double)dotProduct));
+ if (debugFlag)
+ debugPrint(" angle from cone direction = " + angle);
+ return(angle);
+ }
+
+ /************
+ *
+ * Calculate Filter
+ *
+ * *****************/
+ /*
+ * Calculates the low-pass cutoff frequency filter value applied to the
+ * a sound based on both:
+ * Distance Filter (from Aural Attributes) based on distance
+ * between the sound and the listeners position
+ * Angular Filter (for Directional Sounds) based on the angle
+ * between a sound's projected direction and the
+ * vector between the sounds position and center ear.
+ * The lowest of these two filter is used.
+ * This filter value is stored into the sample's filterFreq field.
+ */
+ void calculateFilter(float distance, AuralParameters attribs) {
+ // setting filter cutoff freq to 44.1kHz which, in this
+ // implementation, is the same as not performing filtering
+ float distanceFilter = 44100.0f;
+ float angularFilter = 44100.0f;
+ int arrayLength = attribs.getDistanceFilterLength();
+ int filterType = attribs.getDistanceFilterType();
+
+ boolean distanceFilterFound = false;
+ boolean angularFilterFound = false;
+ if ((filterType == AuralParameters.NO_FILTERING) && arrayLength > 0) {
+ double[] distanceArray = new double[arrayLength];
+ float[] cutoffArray = new float[arrayLength];
+ attribs.getDistanceFilter(distanceArray, cutoffArray);
+
+ if (debugFlag) {
+ debugPrint("distanceArray cutoffArray");
+ for (int i=0; i<arrayLength; i++)
+ debugPrint((float)distanceArray[i] + ", " + cutoffArray[i]);
+ }
+
+ // Calculate angle from direction axis towards listener
+ float angle = findAngularOffset();
+ distanceFilter = findFactor((double)angle,
+ angularDistance, angularFilterCutoff);
+ if (distanceFilter < 0.0f)
+ distanceFilterFound = false;
+ else
+ distanceFilterFound = true;
+ }
+ else {
+ distanceFilterFound = false;
+ distanceFilter = -1.0f;
+ }
+
+ if (debugFlag)
+ debugPrint(" calculateFilter arrayLength = " + arrayLength);
+
+ // Angular filter of directional sound sources.
+ arrayLength = angularDistance.length;
+ filterType = angularFilterType;
+ if ((filterType != AuralParameters.NO_FILTERING) && arrayLength > 0) {
+ angularFilter = findFactor((double)distance,
+ angularDistance, angularFilterCutoff);
+ if (angularFilter < 0.0f)
+ angularFilterFound = false;
+ else
+ angularFilterFound = true;
+ }
+ else {
+ angularFilterFound = false;
+ angularFilter = -1.0f;
+ }
+
+ filterFlag = distanceFilterFound || angularFilterFound;
+ if (distanceFilter < 0.0f)
+ filterFreq = angularFilter;
+ else if (angularFilter < 0.0f)
+ filterFreq = distanceFilter;
+ else // both filter frequencies are > 0
+ filterFreq = Math.min(distanceFilter, angularFilter);
+
+ if (debugFlag)
+ debugPrint(" calculateFilter flag,freq = " + filterFlag +
+ "," + filterFreq );
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/audioengines/javasound/JSMidi.java b/src/classes/share/com/sun/j3d/audioengines/javasound/JSMidi.java
new file mode 100755
index 0000000..0ec687e
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/audioengines/javasound/JSMidi.java
@@ -0,0 +1,67 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.audioengines.javasound;
+
+/**
+ * The JSMidi class defines audio output methods that call the JavaSound
+ * API methods for MIDI sounds.
+ *
+ * <p>
+ * NOTE: This class is not yet implemented.
+ */
+
+class JSMidi extends JSChannel {
+ private static boolean warningReported = false;
+
+ JSMidi() {
+ // Report a "not implemented" warning message
+ if (!warningReported) {
+ System.err.println("***");
+ System.err.println("*** WARNING: JavaSoundMixer: MIDI sound not implemented");
+ System.err.println("***");
+ warningReported = true;
+ }
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/audioengines/javasound/JSPositionalSample.java b/src/classes/share/com/sun/j3d/audioengines/javasound/JSPositionalSample.java
new file mode 100755
index 0000000..580c060
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/audioengines/javasound/JSPositionalSample.java
@@ -0,0 +1,1339 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+/*
+ * Java Sound PositionalSample object
+ *
+ * IMPLEMENTATION NOTE: The JavaSoundMixer is incomplete and really needs
+ * to be rewritten.
+ */
+
+package com.sun.j3d.audioengines.javasound;
+
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import com.sun.j3d.audioengines.*;
+
+/**
+ * The PostionalSample Class defines the data and methods associated with a
+ * PointSound sample played thru the AudioDevice.
+ */
+
+class JSPositionalSample extends JSSample
+{
+
+ // maintain fields for stereo channel rendering
+ float leftGain = 1.0f; // scale factor
+ float rightGain = 1.0f; // scale factor
+ int leftDelay = 0; // left InterauralTimeDifference in millisec
+ int rightDelay = 0; // right ITD in millisec
+ // fields for reverb channel
+
+ // debug flag for the verbose Doppler calculation methods
+ static final
+ protected boolean dopplerFlag = true;
+
+ /**
+ * For positional and directional sounds, TWO Hae streams or clips
+ * are allocated, one each for the left and right channels, played at
+ * a different (delayed) time and with a different gain value.
+ */
+ int secondIndex = NULL_SAMPLE;
+ /**
+ * A third sample for control of reverb of the stream/clip is openned
+ * and maintained for all directional/positional sounds.
+ * For now, even if no aural attributes (using reverb) are active,
+ * a reverb channel is always started with the other two. A sound could
+ * be started without reverb and then reverb added later, but since there
+ * is no way to offset properly into all sounds (considering non-cached
+ * and nconsistent rate-changes during playing) this third sound is
+ * always allocated and started.
+ */
+ int reverbIndex = NULL_SAMPLE;
+
+ /**
+ * Save ear positions transformed into VirtualWorld coords from Head coords
+ * These default positions are used when the real values cannot queried
+ */
+ Point3f xformLeftEar = new Point3f(-0.09f, -0.03f, 0.095f);
+ Point3f xformRightEar = new Point3f(0.09f, -0.03f, 0.095f);
+ // Z axis in head space - looking into the screen
+ Vector3f xformHeadZAxis = new Vector3f(0.0f, 0.0f, -1.0f); // Va
+
+ /**
+ * Save vectors from source source position to transformed ear parameters
+ */
+ Vector3f sourceToCenterEar = new Vector3f(); // Vh
+ Vector3f sourceToRightEar = new Vector3f(); // Vf or Vc
+ Vector3f sourceToLeftEar = new Vector3f(); // Vf or Vc
+
+ boolean averageDistances = false;
+ long deltaTime = 0;
+ double sourcePositionChange = -1.0;
+ double headPositionChange = -1.0;
+
+ /*
+ * Maintain the last locations of sound and head as well as time the
+ * sound was last processed.
+ * Process delta distance and time as part of Doppler calculations.
+ */
+ static int MAX_DISTANCES = 4;
+ int numDistances = 0;
+// TODO: time is based on changes to position!!! only
+// TODO: must shap shot when either Position OR ear changes!!!
+// TODO: must grab all changes to VIEW parameters (could change ear)!!!
+// not just when pointer to View changes!!
+ long[] times = new long[MAX_DISTANCES];
+ Point3f[] positions = new Point3f[MAX_DISTANCES]; // xformed sound source positions
+ Point3f[] centerEars = new Point3f[MAX_DISTANCES]; // xformed center ear positions
+ /*
+ * a set of indices (first, last, and current) are maintained to point
+ * into the above arrays
+ */
+ int firstIndex = 0;
+ int lastIndex = 0;
+ int currentIndex = 0;
+
+ /*
+ * Allow changes in Doppler rate only small incremental values otherwise
+ * you hear skips in the pitch of a sound during playback.
+ * When playback is faster, allow delta changes:
+ * (diff in Factor for octave (1.0))/(12 1/2-steps))*(1/4) of half-step
+ * When playback is slower, allow delta changes:
+ * (diff in Factor for octave (0.5))/(12 1/2-steps))*(1/4) of half-step
+ */
+ double lastRequestedDopplerRateRatio = -1.0f;
+ double lastActualDopplerRateRatio = -1.0f;
+ static double maxRatio = 256.0f; // 8 times higher/lower
+ /*
+ * denotes movement of sound away or towards listener
+ */
+ static int TOWARDS = 1;
+ static int NO_CHANGE = 0;
+ static int AWAY = -1;
+
+ /*
+ * Process request for Filtering fields
+ */
+ boolean filterFlag = false;
+ float filterFreq = -1.0f;
+
+ /*
+ * Construct a new audio device Sample object
+ */
+ public JSPositionalSample() {
+ super();
+ if (debugFlag)
+ debugPrint("JSPositionalSample constructor");
+ // initiallize circular buffer for averaging distance values
+ for (int i=0; i<MAX_DISTANCES; i++) {
+ positions[i] = new Point3f();
+ centerEars[i] = new Point3f(0.09f, -0.03f, 0.095f);
+ }
+ clear();
+ }
+
+ // TODO: get/set secondChannel to JSStream/Clip/MIDI
+ // TODO: get/set reverbChannel to JSStream/Clip/MIDI
+ /*
+ * Process request for Filtering fields
+ */
+ boolean getFilterFlag() {
+ return filterFlag;
+ }
+ float getFilterFreq() {
+ return filterFreq;
+ }
+
+
+ /**
+ * Clears the fields associated with sample data for this sound, and
+ * frees any device specific data associated with this sample.
+ */
+ public void clear() {
+ if (debugFlag)
+ debugPrint("JSPositionalSample.clear() enter");
+ super.clear();
+ leftGain = 1.0f;
+ rightGain = 1.0f;
+ leftDelay = 0;
+ rightDelay = 0;
+ xformLeftEar.set(-0.09f, -0.03f, 0.095f);
+ xformRightEar.set(0.09f, -0.03f, 0.095f);
+ // Z axis in head space - looking into the screen
+ xformHeadZAxis.set(0.0f, 0.0f, -1.0f); // Va
+ sourceToCenterEar.set(0.0f, 0.0f, 0.0f); // Vh
+ sourceToRightEar.set(0.0f, 0.0f, 0.0f); // Vf or Vc
+ sourceToLeftEar.set(0.0f, 0.0f, 0.0f); // Vf or Vc
+ reset();
+ if (debugFlag)
+ debugPrint("JSPositionalSample.clear() exit");
+ }
+
+ /**
+ * Reset time and count based fields associated with sample data
+ * for this sound
+ */
+ void reset() {
+ if (debugFlag)
+ debugPrint("JSPositionalSample.reset() enter");
+ super.reset();
+ averageDistances = false; // denotes not previously processed
+ deltaTime = 0;
+ sourcePositionChange = -1.0;
+ headPositionChange = -1.0;
+ rateRatio = 1.0f;
+ numDistances = 0;
+ averageDistances = false;
+ if (debugFlag)
+ debugPrint("JSPositionalSample.reset() exit");
+ }
+ // increments index counters and bumps index numbers if the end of
+ // the circular buffer is reached
+ void incrementIndices() {
+ int maxIndex = MAX_DISTANCES - 1;
+ if (numDistances < maxIndex) {
+ averageDistances = false;
+ currentIndex = numDistances;
+ lastIndex = currentIndex - 1;
+ firstIndex = 0;
+ numDistances++;
+ }
+ else if (numDistances == maxIndex) {
+ // we filled the data buffers completely and are ready to
+ // calculate averages
+ averageDistances = true;
+ currentIndex = maxIndex;
+ lastIndex = currentIndex - 1;
+ firstIndex = 0;
+ numDistances++;
+ }
+ else if (numDistances > maxIndex) {
+ // increment each counter and loop around
+ averageDistances = true;
+ currentIndex++;
+ lastIndex++;
+ firstIndex++;
+ currentIndex %= MAX_DISTANCES;
+ lastIndex %= MAX_DISTANCES;
+ firstIndex %= MAX_DISTANCES;
+ }
+ }
+
+ // Not only do we transform position but delta time is calculated and
+ // old transformed position is saved
+ // Average the last MAX_DISTANCES delta time and change in position using
+ // an array for both and circlularly storing the time and distance values
+ // into this array.
+ // Current transformed position and time in stored into maxIndex of their
+ // respective arrays.
+ void setXformedPosition() {
+ Point3f newPosition = new Point3f();
+ if (debugFlag)
+ debugPrint("*** setXformedPosition");
+ // xform Position
+ if (getVWrldXfrmFlag()) {
+ if (debugFlag)
+ debugPrint(" Transform set so transform pos");
+ vworldXfrm.transform(position, newPosition);
+ }
+ else {
+ if (debugFlag)
+ debugPrint(" Transform NOT set so pos => xformPos");
+ newPosition.set(position);
+ }
+ // store position and increment indices ONLY if theres an actual change
+ if (newPosition.x == positions[currentIndex].x &&
+ newPosition.y == positions[currentIndex].y &&
+ newPosition.z == positions[currentIndex].z ) {
+ if (debugFlag)
+ debugPrint(" No change in pos, so don't reset");
+ return;
+ }
+
+ incrementIndices();
+ // store new transformed position
+ times[currentIndex] = System.currentTimeMillis();
+ positions[currentIndex].set(newPosition);
+ if (debugFlag)
+ debugPrint(" xform(sound)Position -" +
+ " positions[" + currentIndex + "] = (" +
+ positions[currentIndex].x + ", " +
+ positions[currentIndex].y + ", " +
+ positions[currentIndex].z + ")");
+
+ // since this is a change to the sound position and not the
+ // head save the last head position into the current element
+ if (numDistances > 1)
+ centerEars[currentIndex].set(centerEars[lastIndex]);
+
+ }
+
+ /**
+ * Set Doppler effect Rate
+ *
+ * Calculate the rate of change in for the head and sound
+ * between the two time stamps (last two times position or
+ * VirtualWorld transform was updated).
+ * First determine if the head and sound source are moving
+ * towards each other (distance between them is decreasing),
+ * moving away from each other (distance between them is
+ * increasing), or no change (distance is the same, not moving
+ * or moving the same speed/direction).
+ * The following equation is used for determining the change in frequency -
+ * If there has been a change in the distance between the head and sound:
+ *
+ * f' = f * frequencyScaleFactor * velocityRatio
+ *
+ * For no change in the distance bewteen head and sound, velocityRatio is 1:
+ *
+ * f' = f
+ *
+ * For head and sound moving towards each other, velocityRatio (> 1.0) is:
+ *
+ * | speedOfSound*rollOff + velocityOfHead*velocityScaleFactor |
+ * | ------------------------------------------------------------- |
+ * | speedOfSound*rollOff - velocityOfSource*velocityScaleFactor |
+ *
+ * For head and sound moving away from each other, velocityRatio (< 1.0) is:
+ *
+ * | speedOfSound*rollOff - velocityOfHead*velocityScaleFactor |
+ * | ------------------------------------------------------------- |
+ * | speedOfSound*rollOff + velocityOfSource*velocityScaleFactor |
+ *
+ * where frequencyScaleFactor, rollOff, velocityScaleFactor all come from
+ * the active AuralAttributes parameters.
+ * The following special cases must be test for AuralAttribute parameters:
+ * rolloff
+ * Value MUST be > zero for any sound to be heard!
+ * If value is zero, all sounds affected by AuralAttribute region are silent.
+ * velocityScaleFactor
+ * Value MUST be > zero for any sound to be heard!
+ * If value is zero, all sounds affected by AuralAttribute region are paused.
+ * frequencyScaleFactor
+ * Value of zero disables Doppler calculations:
+ * Sfreq' = Sfreq * frequencyScaleFactor
+ *
+ * This rate is passed to device drive as a change to playback sample
+ * rate, in this case the frequency need not be known.
+ *
+ * Return value of zero denotes no change
+ * Return value of -1 denotes ERROR
+ */
+ float calculateDoppler(AuralParameters attribs) {
+ double sampleRateRatio = 1.0;
+ double headVelocity = 0.0; // in milliseconds
+ double soundVelocity = 0.0; // in milliseconds
+ double distanceSourceToHead = 0.0; // in meters
+ double lastDistanceSourceToHead = 0.0; // in meters
+ float speedOfSound = attribs.SPEED_OF_SOUND;
+ double numerator = 1.0;
+ double denominator = 1.0;
+ int direction = NO_CHANGE; // sound movement away or towards listener
+
+ Point3f lastXformPosition;
+ Point3f lastXformCenterEar;
+ Point3f xformPosition;
+ Point3f xformCenterEar;
+ float averagedSoundDistances = 0.0f;
+ float averagedEarsDistances = 0.0f;
+
+ /*
+ * Average the differences between the last MAX_DISTANCE
+ * sound positions and head positions
+ */
+ if (!averageDistances) {
+ // TODO: Use some EPSilion to do 'equals' test against
+ if (dopplerFlag)
+ debugPrint("JSPositionalSample.calculateDoppler - " +
+ "not enough distance data collected, " +
+ "dopplerRatio set to zero");
+ // can't calculate change in direction
+ return 0.0f; // sample rate ratio is zero
+ }
+
+ lastXformPosition = positions[lastIndex];
+ lastXformCenterEar = centerEars[lastIndex];
+ xformPosition = positions[currentIndex];
+ xformCenterEar = centerEars[currentIndex];
+ distanceSourceToHead = xformPosition.distance(xformCenterEar);
+ lastDistanceSourceToHead = lastXformPosition.distance(lastXformCenterEar);
+ if (dopplerFlag) {
+ debugPrint("JSPositionalSample.calculateDoppler - distances: " +
+ "current,last = " + distanceSourceToHead + ", " +
+ lastDistanceSourceToHead );
+ debugPrint(" " +
+ "current position = " +
+ xformPosition.x + ", " + xformPosition.y +
+ ", " + xformPosition.z);
+ debugPrint(" " +
+ "current ear = " +
+ xformCenterEar.x + ", " + xformCenterEar.y +
+ ", " + xformCenterEar.z);
+ debugPrint(" " +
+ "last position = " +
+ lastXformPosition.x + ", " + lastXformPosition.y +
+ ", " + lastXformPosition.z);
+ debugPrint(" " +
+ "last ear = " +
+ lastXformCenterEar.x + ", " + lastXformCenterEar.y +
+ ", " + lastXformCenterEar.z);
+ }
+ if (distanceSourceToHead == lastDistanceSourceToHead) {
+ // TODO: Use some EPSilion to do 'equals' test against
+ if (dopplerFlag)
+ debugPrint("JSPositionalSample.calculateDoppler - " +
+ "distance diff = 0, dopplerRatio set to zero");
+ // can't calculate change in direction
+ return 0.0f; // sample rate ratio is zero
+ }
+
+ deltaTime = times[currentIndex] - times[firstIndex];
+ for (int i=0; i<(MAX_DISTANCES-1); i++) {
+ averagedSoundDistances += positions[i+1].distance(positions[i]);
+ averagedEarsDistances += centerEars[i+1].distance(centerEars[i]);
+ }
+ averagedSoundDistances /= (MAX_DISTANCES-1);
+ averagedEarsDistances /= (MAX_DISTANCES-1);
+ soundVelocity = averagedSoundDistances/deltaTime;
+ headVelocity = averagedEarsDistances/deltaTime;
+ if (dopplerFlag) {
+ debugPrint(" " +
+ "delta time = " + deltaTime );
+ debugPrint(" " +
+ "soundPosition delta = " +
+ xformPosition.distance(lastXformPosition));
+ debugPrint(" " +
+ "soundVelocity = " + soundVelocity);
+ debugPrint(" " +
+ "headPosition delta = " +
+ xformCenterEar.distance(lastXformCenterEar));
+ debugPrint(" " +
+ "headVelocity = " + headVelocity);
+ }
+ if (attribs != null) {
+
+ float rolloff = attribs.rolloff;
+ float velocityScaleFactor = attribs.velocityScaleFactor;
+ if (rolloff != 1.0f) {
+ speedOfSound *= rolloff;
+ if (dopplerFlag)
+ debugPrint(" " +
+ "attrib rollof = " + rolloff);
+ }
+ if (velocityScaleFactor != 1.0f) {
+ soundVelocity *= velocityScaleFactor;
+ headVelocity *= velocityScaleFactor;
+ if (dopplerFlag) {
+ debugPrint(" " +
+ "attrib velocity scale factor = " +
+ velocityScaleFactor );
+ debugPrint(" " +
+ "new soundVelocity = " + soundVelocity);
+ debugPrint(" " +
+ "new headVelocity = " + headVelocity);
+ }
+ }
+ }
+ if (distanceSourceToHead < lastDistanceSourceToHead) {
+ // sound and head moving towards each other
+ if (dopplerFlag)
+ debugPrint(" " +
+ "moving towards...");
+ direction = TOWARDS;
+ numerator = speedOfSound + headVelocity;
+ denominator = speedOfSound - soundVelocity;
+ }
+ else {
+ // sound and head moving away from each other
+ // note: no change in distance case covered above
+ if (dopplerFlag)
+ debugPrint(" " +
+ "moving away...");
+ direction = AWAY;
+ numerator = speedOfSound - headVelocity;
+ denominator = speedOfSound + soundVelocity;
+ }
+ if (numerator <= 0.0) {
+ if (dopplerFlag)
+ debugPrint("JSPositionalSample.calculateDoppler: " +
+ "BOOM!! - velocity of head > speed of sound");
+ return -1.0f;
+ }
+ else if (denominator <= 0.0) {
+ if (dopplerFlag)
+ debugPrint("JSPositionalSample.calculateDoppler: " +
+ "BOOM!! - velocity of sound source negative");
+ return -1.0f;
+ }
+ else {
+ if (dopplerFlag)
+ debugPrint("JSPositionalSample.calculateDoppler: " +
+ "numerator = " + numerator +
+ ", denominator = " + denominator );
+ sampleRateRatio = numerator / denominator;
+ }
+
+/********
+ IF direction WERE important to calling method...
+ * Return value greater than 0 denotes direction of sound source is
+ * towards the listener
+ * Return value less than 0 denotes direction of sound source is
+ * away from the listener
+ if (direction == AWAY)
+ return -((float)sampleRateRatio);
+ else
+ return (float)sampleRateRatio;
+*********/
+ return (float)sampleRateRatio;
+ }
+
+ void updateEar(int dirtyFlags, View view) {
+ if (debugFlag)
+ debugPrint("*** updateEar fields");
+ // xform Ear
+ Point3f xformCenterEar = new Point3f();
+ if (!calculateNewEar(dirtyFlags, view, xformCenterEar)) {
+ if (debugFlag)
+ debugPrint("calculateNewEar returned false");
+ return;
+ }
+ // store ear and increment indices ONLY if there is an actual change
+ if (xformCenterEar.x == centerEars[currentIndex].x &&
+ xformCenterEar.y == centerEars[currentIndex].y &&
+ xformCenterEar.z == centerEars[currentIndex].z ) {
+ if (debugFlag)
+ debugPrint(" No change in ear, so don't reset");
+ return;
+ }
+ // store xform Ear
+ incrementIndices();
+ times[currentIndex] = System.currentTimeMillis();
+ centerEars[currentIndex].set(xformCenterEar);
+ // since this is a change to the head position and not the sound
+ // position save the last sound position into the current element
+ if (numDistances > 1)
+ positions[currentIndex].set(positions[lastIndex]);
+ }
+
+ boolean calculateNewEar(int dirtyFlags, View view, Point3f xformCenterEar) {
+ /*
+ * Transform ear position (from Head) into Virtual World Coord space
+ */
+ Point3d earPosition = new Point3d(); // temporary double Point
+
+ // TODO: check dirty flags coming in
+ // For now, recalculate ear positions by forcing earsXformed false
+ boolean earsXformed = false;
+ if (!earsXformed) {
+ if (view != null) {
+ PhysicalBody body = view.getPhysicalBody();
+ if (body != null) {
+
+ // Get Head Coord. to Virtual World transform
+ // TODO: re-enable this when userHeadToVworld is
+ // implemented correctly!!!
+ Transform3D headToVwrld = new Transform3D();
+ view.getUserHeadToVworld(headToVwrld);
+ if (debugFlag) {
+ debugPrint("user head to Vwrld colum-major:");
+ double[] matrix = new double[16];
+ headToVwrld.get(matrix);
+ debugPrint("JSPosSample " + matrix[0]+", " +
+ matrix[1]+", "+matrix[2]+", "+matrix[3]);
+ debugPrint("JSPosSample " + matrix[4]+", " +
+ matrix[5]+", "+matrix[6]+", "+matrix[7]);
+ debugPrint("JSPosSample " + matrix[8]+", " +
+ matrix[9]+", "+matrix[10]+", "+matrix[11]);
+ debugPrint("JSPosSample " + matrix[12]+", " +
+ matrix[13]+", "+matrix[14]+", "+matrix[15]);
+ }
+
+ // Get left and right ear positions in Head Coord.s
+ // Transforms left and right ears to Virtual World coord.s
+ body.getLeftEarPosition(earPosition);
+ xformLeftEar.x = (float)earPosition.x;
+ xformLeftEar.y = (float)earPosition.y;
+ xformLeftEar.z = (float)earPosition.z;
+ body.getRightEarPosition(earPosition);
+ xformRightEar.x = (float)earPosition.x;
+ xformRightEar.y = (float)earPosition.y;
+ xformRightEar.z = (float)earPosition.z;
+ headToVwrld.transform(xformRightEar);
+ headToVwrld.transform(xformLeftEar);
+ // Transform head viewing (Z) axis to Virtual World coord.s
+ xformHeadZAxis.set(0.0f, 0.0f, -1.0f); // Va
+ headToVwrld.transform(xformHeadZAxis);
+
+ // calculate the new (current) mid-point between the ears
+ // find the mid point between left and right ear positions
+ xformCenterEar.x = xformLeftEar.x +
+ ((xformRightEar.x - xformLeftEar.x)*0.5f);
+ xformCenterEar.y = xformLeftEar.y +
+ ((xformRightEar.y - xformLeftEar.y)*0.5f);
+ xformCenterEar.z = xformLeftEar.z +
+ ((xformRightEar.z - xformLeftEar.z)*0.5f);
+ // TODO: when head changes earDirty should be set!
+ // earDirty = false;
+ if (debugFlag) {
+ debugPrint(" earXformed CALCULATED");
+ debugPrint(" xformCenterEar = " +
+ xformCenterEar.x + " " +
+ xformCenterEar.y + " " +
+ xformCenterEar.z );
+ }
+ earsXformed = true;
+ } // end of body NOT null
+ } // end of view NOT null
+ } // end of earsDirty
+ else {
+ // TODO: use existing transformed ear positions
+ }
+
+ if (!earsXformed) {
+ // uses the default head position of (0.0, -0.03, 0.095)
+ if (debugFlag)
+ debugPrint(" earXformed NOT calculated");
+ }
+ return earsXformed;
+ }
+
+ /**
+ * Render this sample
+ *
+ * Calculate the audiodevice parameters necessary to spatially play this
+ * sound.
+ */
+ public void render(int dirtyFlags, View view, AuralParameters attribs) {
+ if (debugFlag)
+ debugPrint("JSPositionalSample.render");
+ updateEar(dirtyFlags, view);
+
+ /*
+ * Time to check velocities and change the playback rate if necessary...
+ *
+ * Rolloff value MUST be > zero for any sound to be heard!
+ * If rolloff is zero, all sounds affected by AuralAttribute region
+ * are silent.
+ * FrequencyScaleFactor value MUST be > zero for any sound to be heard!
+ * since Sfreq' = Sfreq * frequencyScaleFactor.
+ * If FrequencyScaleFactor is zero, all sounds affected by
+ * AuralAttribute region are paused.
+ * VelocityScaleFactor value of zero disables Doppler calculations.
+ *
+ * Scale 'Doppler' rate (or lack of Doppler) by frequencyScaleFactor.
+ */
+ float dopplerRatio = 1.0f;
+ if (attribs != null) {
+ float rolloff = attribs.rolloff;
+ float frequencyScaleFactor = attribs.frequencyScaleFactor;
+ float velocityScaleFactor = attribs.velocityScaleFactor;
+ if (debugFlag || dopplerFlag)
+ debugPrint("JSPositionalSample: attribs NOT null");
+ if (rolloff <= 0.0f) {
+ if (debugFlag)
+ debugPrint(" rolloff = " + rolloff + " <= 0.0" );
+ // TODO: Make sound silent
+ // return ???
+ }
+ else if (frequencyScaleFactor <= 0.0f) {
+ if (debugFlag)
+ debugPrint(" freqScaleFactor = " + frequencyScaleFactor +
+ " <= 0.0" );
+ // TODO: Pause sound silent
+ // return ???
+ }
+ else if (velocityScaleFactor > 0.0f) {
+ if (debugFlag || dopplerFlag)
+ debugPrint(" velocityScaleFactor = " +
+ velocityScaleFactor);
+/*******
+ if (deltaTime > 0) {
+*******/
+ // Doppler can be calculated after the second time
+ // updateXformParams() is executed
+ dopplerRatio = calculateDoppler(attribs);
+
+ if (dopplerRatio == 0.0f) {
+ // dopplerRatio zeroo denotes no changed
+ // TODO: But what if frequencyScaleFactor has changed
+ if (debugFlag) {
+ debugPrint("JSPositionalSample: render: " +
+ "dopplerRatio returned zero; no change");
+ }
+ }
+ else if (dopplerRatio == -1.0f) {
+ // error returned by calculateDoppler
+ if (debugFlag) {
+ debugPrint("JSPositionalSample: render: " +
+ "dopplerRatio returned = " +
+ dopplerRatio + "< 0");
+ }
+ // TODO: Make sound silent
+ // return ???
+ }
+ else if (dopplerRatio > 0.0f) {
+ // rate could be changed
+ rateRatio = dopplerRatio * frequencyScaleFactor *
+ getRateScaleFactor();
+ if (debugFlag) {
+ debugPrint(" scaled by frequencyScaleFactor = " +
+ frequencyScaleFactor );
+ }
+ }
+/******
+ }
+ else {
+ if (debugFlag)
+ debugPrint("deltaTime <= 0 - skip Doppler calc");
+ }
+******/
+ }
+ else { // auralAttributes not null but velocityFactor <= 0
+ // Doppler is disabled
+ rateRatio = frequencyScaleFactor * getRateScaleFactor();
+ }
+ }
+ /*
+ * since aural attributes undefined, default values are used,
+ * thus no Doppler calculated
+ */
+ else {
+ if (debugFlag || dopplerFlag)
+ debugPrint("JSPositionalSample: attribs null");
+ rateRatio = 1.0f;
+ }
+
+ this.panSample(attribs);
+ }
+
+ /* *****************
+ *
+ * Calculate Angular Gain
+ *
+ * *****************/
+ /*
+ * Calculates the Gain scale factor applied to the overall gain for
+ * a sound based on angle between a sound's projected direction and the
+ * vector between the sounds position and center ear.
+ *
+ * For Point Sounds this value is always 1.0f.
+ */
+ float calculateAngularGain() {
+ return(1.0f);
+ }
+
+ /* *****************
+ *
+ * Calculate Filter
+ *
+ * *****************/
+ /*
+ * Calculates the low-pass cutoff frequency filter value applied to the
+ * a sound based on both:
+ * Distance Filter (from Aural Attributes) based on distance
+ * between the sound and the listeners position
+ * Angular Filter (for Directional Sounds) based on the angle
+ * between a sound's projected direction and the
+ * vector between the sounds position and center ear.
+ * The lowest of these two filter is used.
+ * This filter value is stored into the sample's filterFreq field.
+ */
+ void calculateFilter(float distance, AuralParameters attribs) {
+ // setting filter cutoff freq to 44.1kHz which, in this
+ // implementation, is the same as not performing filtering
+ float distanceFilter = 44100.0f;
+ float angularFilter = 44100.0f;
+ int arrayLength = attribs.getDistanceFilterLength();
+ int filterType = attribs.getDistanceFilterType();
+ boolean distanceFilterFound = false;
+ boolean angularFilterFound = false;
+ if ((filterType != AuralParameters.NO_FILTERING) && arrayLength > 0) {
+ double[] distanceArray = new double[arrayLength];
+ float[] cutoffArray = new float[arrayLength];
+ attribs.getDistanceFilter(distanceArray, cutoffArray);
+
+ if (debugFlag) {
+ debugPrint("distanceArray cutoffArray");
+ for (int i=0; i<arrayLength; i++)
+ debugPrint((float)(distanceArray[i]) + ", " + cutoffArray[i]);
+ }
+ distanceFilter = findFactor((double)distance,
+ distanceArray, cutoffArray);
+ if (distanceFilter < 0.0f)
+ distanceFilterFound = false;
+ else
+ distanceFilterFound = true;
+ }
+ else {
+ distanceFilterFound = false;
+ distanceFilter = -1.0f;
+ }
+
+ if (debugFlag)
+ debugPrint(" calculateFilter arrayLength = " + arrayLength);
+
+ // Angular filter only applies to directional sound sources.
+ angularFilterFound = false;
+ angularFilter = -1.0f;
+
+ filterFlag = distanceFilterFound || angularFilterFound;
+ filterFreq = distanceFilter;
+ if (debugFlag)
+ debugPrint(" calculateFilter flag,freq = " + filterFlag +
+ "," + filterFreq );
+ }
+
+ /* *****************
+ *
+ * Find Factor
+ *
+ * *****************/
+ /*
+ * Interpolates the correct output factor given a 'distance' value
+ * and references to the distance array and factor array used in
+ * the calculation. These array parameters could be either linear or
+ * angular distance arrays, or filter arrays.
+ * The values in the distance array are monotonically increasing.
+ * This method looks at pairs of distance array values to find which
+ * pair the input distance argument is between distanceArray[index] and
+ * distanceArray[index+1].
+ * The index is used to get factorArray[index] and factorArray[index+1].
+ * Then the ratio of the 'distance' between this pair of distanceArray
+ * values is used to scale the two found factorArray values proportionally.
+ * The resulting factor is returned, unless there is an error, then -1.0
+ * is returned.
+ */
+ float findFactor(double distance,
+ double[] distanceArray, float[] factorArray) {
+ int index, lowIndex, highIndex, indexMid;
+
+ if (debugFlag)
+ debugPrint("JSPositionalSample.findFactor entered");
+
+ /*
+ * Error checking
+ */
+ if (distanceArray == null || factorArray == null) {
+ if (debugFlag)
+ debugPrint(" findFactor: arrays null");
+ return -1.0f; // no value
+ }
+ int arrayLength = distanceArray.length;
+ if (arrayLength < 2) {
+ if (debugFlag)
+ debugPrint(" findFactor: arrays length < 2");
+ return -1.0f; // no value
+ }
+ int largestIndex = arrayLength - 1;
+
+ /*
+ * Calculate distanceGain scale factor
+ */
+ if (distance >= distanceArray[largestIndex]) {
+ if (debugFlag) {
+ debugPrint(" findFactor: distance > " +
+ distanceArray[largestIndex]);
+ debugPrint(" distanceArray length = "+ arrayLength);
+ }
+ return factorArray[largestIndex];
+ }
+ else if (distance <= distanceArray[0]) {
+ if (debugFlag)
+ debugPrint(" findFactor: distance < " +
+ distanceArray[0]);
+ return factorArray[0];
+ }
+ /*
+ * Distance between points within attenuation array.
+ * Use binary halfing of distance array
+ */
+ else {
+ lowIndex = 0;
+ highIndex = largestIndex;
+ if (debugFlag)
+ debugPrint(" while loop to find index: ");
+ while (lowIndex < (highIndex-1)) {
+ if (debugFlag) {
+ debugPrint(" lowIndex " + lowIndex +
+ ", highIndex " + highIndex);
+ debugPrint(" d.A. pair for lowIndex " +
+ distanceArray[lowIndex] + ", " + factorArray[lowIndex] );
+ debugPrint(" d.A. pair for highIndex " +
+ distanceArray[highIndex] + ", " + factorArray[highIndex] );
+ }
+ /*
+ * we can assume distance is between distance atttenuation vals
+ * distanceArray[lowIndex] and distanceArray[highIndex]
+ * calculate gain scale factor based on distance
+ */
+ if (distanceArray[lowIndex] >= distance) {
+ if (distance < distanceArray[lowIndex]) {
+ if (internalErrors)
+ debugPrint("Internal Error: binary halving in " +
+ " findFactor failed; distance < index value");
+ }
+ if (debugFlag) {
+ debugPrint( " index == distanceGain " +
+ lowIndex);
+ debugPrint(" findFactor returns [LOW=" +
+ lowIndex + "] " + factorArray[lowIndex]);
+ }
+ // take value of scale factor directly from factorArray
+ return factorArray[lowIndex];
+ }
+ else if (distanceArray[highIndex] <= distance) {
+ if (distance > distanceArray[highIndex]) {
+ if (internalErrors)
+ debugPrint("Internal Error: binary halving in " +
+ " findFactor failed; distance > index value");
+ }
+ if (debugFlag) {
+ debugPrint( " index == distanceGain " +
+ highIndex);
+ debugPrint(" findFactor returns [HIGH=" +
+ highIndex + "] " + factorArray[highIndex]);
+ }
+ // take value of scale factor directly from factorArray
+ return factorArray[highIndex];
+ }
+ if (distance > distanceArray[lowIndex] &&
+ distance < distanceArray[highIndex] ) {
+ indexMid = lowIndex + ((highIndex - lowIndex) / 2);
+ if (distance <= distanceArray[indexMid])
+ // value of distance in lower "half" of list
+ highIndex = indexMid;
+ else // value if distance in upper "half" of list
+ lowIndex = indexMid;
+ }
+ } /* of while */
+
+ /*
+ * ratio: distance from listener to sound source
+ * between lowIndex and highIndex times
+ * attenuation value between lowIndex and highIndex
+ * gives linearly interpolationed attenuation value
+ */
+ if (debugFlag) {
+ debugPrint( " ratio calculated using lowIndex " +
+ lowIndex + ", highIndex " + highIndex);
+ debugPrint( " d.A. pair for lowIndex " +
+ distanceArray[lowIndex]+", "+factorArray[lowIndex] );
+ debugPrint( " d.A. pair for highIndex " +
+ distanceArray[highIndex]+", "+factorArray[highIndex] );
+ }
+
+ float outputFactor =
+ ((float)(((distance - distanceArray[lowIndex])/
+ (distanceArray[highIndex] - distanceArray[lowIndex]) ) ) *
+ (factorArray[highIndex] - factorArray[lowIndex]) ) +
+ factorArray[lowIndex] ;
+ if (debugFlag)
+ debugPrint(" findFactor returns " + outputFactor);
+ return outputFactor;
+ }
+ }
+
+ /**
+ * CalculateDistanceAttenuation
+ *
+ * Simply calls generic (for PointSound) 'findFactor()' with
+ * a single set of attenuation distance and gain scale factor arrays.
+ */
+ float calculateDistanceAttenuation(float distance) {
+ float factor = 1.0f;
+ factor = findFactor((double)distance, this.attenuationDistance,
+ this.attenuationGain);
+ if (factor >= 0.0)
+ return (factor);
+ else
+ return (1.0f);
+ }
+
+ /* ******************
+ *
+ * Pan Sample
+ *
+ * ******************/
+ /*
+ * Sets pan and delay for a single sample associated with this Sound.
+ * Front and Back quadrants are treated the same.
+ */
+ void panSample(AuralParameters attribs) {
+ int quadrant = 1;
+ float intensityHigh = 1.0f;
+ float intensityLow = 0.125f;
+ float intensityDifference = intensityHigh - intensityLow;
+
+ //TODO: time around "average" default head
+ // int delayHigh = 32; // 32.15 samples = .731 ms
+ // int delayLow = 0;
+
+ float intensityOffset; // 0.0 -> 1.0 then 1.0 -> 0.0 for full rotation
+ float halfX;
+ int id;
+ int err;
+
+ float nearZero = 0.000001f;
+ float nearOne = 0.999999f;
+ float nearNegativeOne = -nearOne;
+ float halfPi = (float)Math.PI * 0.5f;
+ /*
+ * Parameters used for IID and ITD equations.
+ * Name of parameters (as used in Guide, E.3) are denoted in comments.
+ */
+ float distanceSourceToCenterEar = 0.0f; // Dh
+ float lastDistanceSourceToCenterEar = 0.0f;
+ float distanceSourceToRightEar = 0.0f; // Ef or Ec
+ float distanceSourceToLeftEar = 0.0f; // Ef or Ec
+ float distanceBetweenEars = 0.18f; // De
+ float radiusOfHead = 0.0f; // De/2
+ float radiusOverDistanceToSource = 0.0f; // De/2 * 1/Dh
+
+ float alpha = 0.0f; // 'alpha'
+ float sinAlpha = 0.0f; // sin(alpha);
+ float gamma = 0.0f; // 'gamma'
+
+ // Speed of Sound (unaffected by rolloff) in millisec/meters
+ float speedOfSound = attribs.SPEED_OF_SOUND;
+ float invSpeedOfSound = 1.0f / attribs.SPEED_OF_SOUND;
+
+ float sampleRate = 44.1f; // 44 samples/millisec
+
+ boolean rightEarClosest = false;
+ boolean soundFromBehind = false;
+
+ float distanceGain = 1.0f;
+ float allGains = this.gain; // product of gain scale factors
+
+ Point3f workingPosition = new Point3f();
+ Point3f workingCenterEar = new Point3f();
+
+ // Asuumes that head and ear positions can be retrieved from universe
+
+ Vector3f mixScale = new Vector3f(); // for mix*Samples code
+
+ // Use transformed position of this sound
+ workingPosition.set(positions[currentIndex]);
+ workingCenterEar.set(centerEars[currentIndex]);
+ if (debugFlag) {
+ debugPrint("panSample:workingPosition from" +
+ " positions["+currentIndex+"] -> " +
+ workingPosition.x + ", " + workingPosition.y + ", " +
+ workingPosition.z + " for pointSound " + this);
+ debugPrint("panSample:workingCenterEar " +
+ workingCenterEar.x + " " + workingCenterEar.y + " " +
+ workingCenterEar.z);
+ debugPrint("panSample:xformLeftEar " +
+ xformLeftEar.x + " " + xformLeftEar.y + " " +
+ xformLeftEar.z);
+ debugPrint("panSample:xformRightEar " +
+ xformRightEar.x + " " + xformRightEar.y + " " +
+ xformRightEar.z);
+ }
+
+ // Create the vectors from the sound source to head positions
+ sourceToCenterEar.x = workingCenterEar.x - workingPosition.x;
+ sourceToCenterEar.y = workingCenterEar.y - workingPosition.y;
+ sourceToCenterEar.z = workingCenterEar.z - workingPosition.z;
+ sourceToRightEar.x = xformRightEar.x - workingPosition.x;
+ sourceToRightEar.y = xformRightEar.y - workingPosition.y;
+ sourceToRightEar.z = xformRightEar.z - workingPosition.z;
+ sourceToLeftEar.x = xformLeftEar.x - workingPosition.x;
+ sourceToLeftEar.y = xformLeftEar.y - workingPosition.y;
+ sourceToLeftEar.z = xformLeftEar.z - workingPosition.z;
+
+ /*
+ * get distances from SoundSource to
+ * (i) head origin
+ * (ii) right ear
+ * (iii) left ear
+ */
+ distanceSourceToCenterEar = workingPosition.distance(workingCenterEar);
+ distanceSourceToRightEar = workingPosition.distance(xformRightEar);
+ distanceSourceToLeftEar = workingPosition.distance(xformLeftEar);
+ distanceBetweenEars = xformRightEar.distance(xformLeftEar);
+ if (debugFlag)
+ debugPrint(" distance from left,right ears to source: = (" +
+ distanceSourceToLeftEar + ", " + distanceSourceToRightEar + ")");
+
+ radiusOfHead = distanceBetweenEars * 0.5f;
+ if (debugFlag)
+ debugPrint(" radius of head = " + radiusOfHead );
+ radiusOverDistanceToSource = // De/2 * 1/Dh
+ radiusOfHead/distanceSourceToCenterEar;
+ if (debugFlag)
+ debugPrint(" radius over distance = " + radiusOverDistanceToSource );
+ if (debugFlag) {
+ debugPrint("panSample:source to center ear " +
+ sourceToCenterEar.x + " " + sourceToCenterEar.y + " " +
+ sourceToCenterEar.z );
+ debugPrint("panSample:xform'd Head ZAxis " +
+ xformHeadZAxis.x + " " + xformHeadZAxis.y + " " +
+ xformHeadZAxis.z );
+ debugPrint("panSample:length of sourceToCenterEar " +
+ sourceToCenterEar.length());
+ debugPrint("panSample:length of xformHeadZAxis " +
+ xformHeadZAxis.length());
+ }
+
+ // Dot Product
+ double dotProduct = (double)(
+ (sourceToCenterEar.dot(xformHeadZAxis))/
+ (sourceToCenterEar.length() * xformHeadZAxis.length()));
+ if (debugFlag)
+ debugPrint( " dot product = " + dotProduct );
+ alpha = (float)(Math.acos(dotProduct));
+ if (debugFlag)
+ debugPrint( " alpha = " + alpha );
+
+ if (alpha > halfPi) {
+ if (debugFlag)
+ debugPrint(" sound from behind");
+ soundFromBehind = true;
+ alpha = (float)Math.PI - alpha;
+ if (debugFlag)
+ debugPrint( " PI minus alpha =>" + alpha );
+ }
+ else {
+ soundFromBehind = false;
+ if (debugFlag)
+ debugPrint(" sound from in front");
+ }
+
+ gamma = (float)(Math.acos(radiusOverDistanceToSource));
+ if (debugFlag)
+ debugPrint( " gamma " + gamma );
+
+ rightEarClosest =
+ (distanceSourceToRightEar>distanceSourceToLeftEar) ? false : true ;
+ /*
+ * Determine the quadrant sound is in
+ */
+ if (rightEarClosest) {
+ if (debugFlag)
+ debugPrint( " right ear closest");
+ if (soundFromBehind)
+ quadrant = 4;
+ else
+ quadrant = 1;
+ }
+ else {
+ if (debugFlag)
+ debugPrint( " left ear closest");
+ if (soundFromBehind)
+ quadrant = 3;
+ else
+ quadrant = 2;
+ }
+ sinAlpha = (float)(Math.sin((double)alpha));
+ if (sinAlpha < 0.0) sinAlpha = -sinAlpha;
+ if (debugFlag)
+ debugPrint( " sin(alpha) " + sinAlpha );
+
+ /*
+ * The path from sound source to the farthest ear is always indirect
+ * (it wraps around part of the head).
+ * Calculate distance wrapped around the head for farthest ear
+ */
+ float DISTANCE = (float)Math.sqrt((double)
+ distanceSourceToCenterEar * distanceSourceToCenterEar +
+ radiusOfHead * radiusOfHead);
+ if (debugFlag)
+ debugPrint( " partial distance from edge of head to source = "
+ + distanceSourceToCenterEar);
+ if (rightEarClosest) {
+ distanceSourceToLeftEar =
+ DISTANCE + radiusOfHead * (halfPi+alpha-gamma);
+ if (debugFlag)
+ debugPrint(" new distance from left ear to source = "
+ + distanceSourceToLeftEar);
+ }
+ else {
+ distanceSourceToRightEar =
+ DISTANCE + radiusOfHead * (halfPi+alpha-gamma);
+ if (debugFlag)
+ debugPrint(" new distance from right ear to source = "
+ + distanceSourceToRightEar);
+ }
+ /*
+ * The path from the source source to the closest ear could either
+ * be direct or indirect (wraps around part of the head).
+ * if sinAlpha >= radiusOverDistance path of sound to closest ear
+ * is direct, otherwise it is indirect
+ */
+ if (sinAlpha < radiusOverDistanceToSource) {
+ if (debugFlag)
+ debugPrint(" closest path is also indirect ");
+ // Path of sound to closest ear is indirect
+
+ if (rightEarClosest) {
+ distanceSourceToRightEar =
+ DISTANCE + radiusOfHead * (halfPi-alpha-gamma);
+ if (debugFlag)
+ debugPrint(" new distance from right ear to source = "
+ + distanceSourceToRightEar);
+ }
+ else {
+ distanceSourceToLeftEar =
+ DISTANCE + radiusOfHead * (halfPi-alpha-gamma);
+ if (debugFlag)
+ debugPrint(" new distance from left ear to source = "
+ + distanceSourceToLeftEar);
+ }
+ }
+ else {
+ if (debugFlag)
+ debugPrint(" closest path is direct ");
+ if (rightEarClosest) {
+ if (debugFlag)
+ debugPrint(" direct distance from right ear to source = "
+ + distanceSourceToRightEar);
+ }
+ else {
+ if (debugFlag)
+ debugPrint(" direct distance from left ear to source = "
+ + distanceSourceToLeftEar);
+ }
+ }
+
+ /**
+ * Short-cut taken. Rather than using actual delays from source
+ * (where the overall distances would be taken into account in
+ * determining delay) the difference in the left and right delay
+ * are applied.
+ * This approach will be preceptibly wrong for sound sources that
+ * are very far away from the listener so both ears would have
+ * large delay.
+ */
+ sampleRate = channel.rateInHz * (0.001f); // rate in milliseconds
+ if (rightEarClosest) {
+ rightDelay = 0;
+ leftDelay = (int)((distanceSourceToLeftEar - distanceSourceToRightEar) *
+ invSpeedOfSound * sampleRate);
+ }
+ else {
+ leftDelay = 0;
+ rightDelay = (int)((distanceSourceToRightEar - distanceSourceToLeftEar) *
+ invSpeedOfSound * sampleRate);
+ }
+
+ if (debugFlag) {
+ debugPrint(" using inverted SoS = " + invSpeedOfSound);
+ debugPrint(" and sample rate = " + sampleRate);
+ debugPrint(" left and right delay = ("
+ + leftDelay + ", " + rightDelay + ")");
+ }
+
+ // What should the gain be for the different ears???
+ // TODO: now using a hack that sets gain based on a unit circle!!!
+ workingPosition.sub(workingCenterEar); // offset sound pos. by head origin
+ // normalize; put Sound on unit sphere around head origin
+ workingPosition.scale(1.0f/distanceSourceToCenterEar);
+ if (debugFlag)
+ debugPrint(" workingPosition after unitization " +
+ workingPosition.x+" "+workingPosition.y+" "+workingPosition.z );
+
+ /*
+ * Get the correct distance gain scale factor from attenuation arrays.
+ * This requires that sourceToCenterEar vector has been calculated.
+ */
+ // TODO: now using distance from center ear to source
+ // Using distances from each ear to source would be more accurate
+ distanceGain = calculateDistanceAttenuation(distanceSourceToCenterEar);
+
+ allGains *= distanceGain;
+
+ /*
+ * Add angular gain (for Cone sound)
+ */
+ if (debugFlag)
+ debugPrint(" all Gains (without angular gain) " + allGains);
+ // assume that transfromed Position is already calculated
+ allGains *= this.calculateAngularGain();
+ if (debugFlag)
+ debugPrint(" (incl. angular gain) " + allGains);
+
+ halfX = workingPosition.x/2.0f;
+ if (halfX >= 0)
+ intensityOffset = (intensityDifference * (0.5f - halfX));
+ else
+ intensityOffset = (intensityDifference * (0.5f + halfX));
+
+ /*
+ * For now have delay constant for front back sound for now
+ */
+ if (debugFlag)
+ debugPrint("panSample: quadrant " + quadrant);
+ switch (quadrant) {
+ case 1:
+ // Sound from front, right of center of head
+ case 4:
+ // Sound from back, right of center of head
+ rightGain = allGains * (intensityHigh - intensityOffset);
+ leftGain = allGains * (intensityLow + intensityOffset);
+ break;
+
+ case 2:
+ // Sound from front, left of center of head
+ case 3:
+ // Sound from back, right of center of head
+ leftGain = allGains * (intensityHigh - intensityOffset);
+ rightGain = allGains * (intensityLow + intensityOffset);
+ break;
+ } /* switch */
+ if (debugFlag)
+ debugPrint("panSample: left/rightGain " + leftGain +
+ ", " + rightGain);
+
+ // Combines distance and angular filter to set this sample's current
+ // frequency cutoff value
+ calculateFilter(distanceSourceToCenterEar, attribs);
+
+ } /* panSample() */
+
+// NOTE: setGain in audioengines.Sample is used to set/get user suppled factor
+// this class uses this single gain value to calculate the left and
+// right gain values
+}
diff --git a/src/classes/share/com/sun/j3d/audioengines/javasound/JSSample.java b/src/classes/share/com/sun/j3d/audioengines/javasound/JSSample.java
new file mode 100755
index 0000000..cc19fda
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/audioengines/javasound/JSSample.java
@@ -0,0 +1,362 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+/*
+ * Java Sound Sample object
+ *
+ * IMPLEMENTATION NOTE: The JavaSoundMixer is incomplete and really needs
+ * to be rewritten.
+ */
+
+package com.sun.j3d.audioengines.javasound;
+
+import java.net.URL;
+import java.io.InputStream;
+import javax.media.j3d.*;
+import javax.sound.sampled.*;
+import com.sun.j3d.audioengines.*;
+
+/**
+ * The Sample Class extended for Java Sound Mixer specific audio device.
+ */
+
+class JSSample extends com.sun.j3d.audioengines.Sample
+{
+ /*
+ * NOTE: for this device type there is exactly one sample associated
+ * with each sound.
+ */
+
+ /**
+ * Sound Data Types
+ *
+ * Samples can be processed as streaming or buffered data.
+ * Fully spatializing sound sources may require data to be buffered.
+ *
+ * Sound data specified as Streaming is not copied by the AudioDevice
+ * driver implementation. It is up the application to ensure that
+ * this data is continuously accessible during sound rendering.
+ * Futhermore, full sound spatialization may not be possible, for
+ * all AudioDevice implementations on unbuffered sound data.
+ */
+ static final int STREAMING_AUDIO_DATA = 1;
+ /**
+ * Sound data specified as Buffered is copied by the AudioDevice
+ * driver implementation.
+ */
+ static final int BUFFERED_AUDIO_DATA = 2;
+ /**
+ * MIDI data
+ * TODO: differentiate between STREAMING and BUFFERED MIDI data
+ * right now all MIDI data is buffered
+ */
+ static final int STREAMING_MIDI_DATA = 3;
+ static final int BUFFERED_MIDI_DATA = 3;
+ static final int UNSUPPORTED_DATA_TYPE = -1;
+
+ static final int NULL_SAMPLE = -1;
+
+ /**
+ * sound data types: BUFFERED (cached) or STREAMING (non-cached)
+ */
+ int dataType = BUFFERED_AUDIO_DATA;
+
+ JSChannel channel = null;
+
+ /**
+ * Offset pointer within currently playing sample data
+ */
+ long dataOffset = 0;
+
+ /*
+ * Maintain continuously playing silent sound sources.
+ */
+ long timeDeactivated = 0;
+ long positionDeactivated = 0;
+
+ long sampleLength = 0;
+ long loopStartOffset = 0; // for most this will be 0
+ long loopLength = 0; // for most this is end sample - sampleLength
+ long attackLength = 0; // portion of sample before loop section
+ long releaseLength = 0; // portion of sample after loop section
+
+ float rateRatio = 1.0f;
+ float currentRateRatio = -1.0f; // last actual rate ratio send to device
+ float targetRateRatio = -1.0f;
+ boolean rampRateFlag = false;
+
+ public JSSample() {
+ super();
+ if (debugFlag)
+ debugPrintln("JSSample constructor");
+ }
+
+ // the only public methods are those declared in the audioengines
+ // package as public
+
+ /*
+ * This excutes code necessary to set current fields to their current
+ * correct values before JavaSoundMixer either start or updates the
+ * sample thru calls to JSThread.
+ */
+ public void render(int dirtyFlags, View view, AuralParameters attribs) {
+ if (debugFlag)
+ debugPrint("JSSample.render ");
+ // if this is starting set gain, delay (for Pos), freq rate ...
+ // TODO: NOT SURE - leaving this in for now
+ float freqScaleFactor = attribs.frequencyScaleFactor;
+ if (attribs != null) {
+ if (freqScaleFactor <= 0.0f) {
+ // TODO: Pause Sample
+ }
+ else
+ rateRatio = currentRateRatio * freqScaleFactor;
+ }
+ else
+ rateRatio = currentRateRatio;
+ }
+
+ /**
+ * Clears/re-initialize fields associated with sample data for
+ * this sound,
+ * and frees any device specific data associated with this sample.
+ */
+ public void clear() {
+ super.clear();
+ if (debugFlag)
+ debugPrintln("JSSample.clear() entered");
+ // TODO: unload sound data at device
+// null out samples element that points to this?
+// would this cause samples list size to shrink?
+// if sample elements are never freed then does this mean
+// a have a memory leak?
+ dataType = UNSUPPORTED_DATA_TYPE;
+ dataOffset = 0;
+ timeDeactivated = 0;
+ positionDeactivated = 0;
+ sampleLength = 0;
+ loopStartOffset = 0;
+ loopLength = 0;
+ attackLength = 0;
+ releaseLength = 0;
+ rateRatio = 1.0f;
+ channel = null;
+ if (debugFlag)
+ debugPrintln("JSSample.clear() exited");
+ }
+
+ // @return error true if error occurred
+ boolean load(MediaContainer soundData) {
+ /**
+ * Get the AudioInputStream first.
+ * MediaContiner passed to method assumed to be a clone of the
+ * application node with the query capability bits set on.
+ */
+ String path = soundData.getURLString();
+ URL url = soundData.getURLObject();
+ InputStream inputStream = soundData.getInputStream();
+ boolean cacheFlag = soundData.getCacheEnable();
+ AudioInputStream ais = null;
+ DataLine dataLine = null;
+
+ // TODO: How do we determine if the file is a MIDI file???
+ // for now set dataType to BUFFERED_ or STREAMING_AUDIO_DATA
+ // used to test for ais instanceof AudioMidiInputStream ||
+ // ais instanceof AudioRmfInputStream )
+ // then set dataType = JSSample.BUFFERED_MIDI_DATA;
+ // QUESTION: can non-cached MIDI files ever be supported ?
+ /****************
+ // TODO: when we have a way to determine data type use code below
+ if (dataType==UNSUPPORTED_DATA_TYPE OR error_occurred)
+ clearSound(index);
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer.prepareSound get dataType failed");
+ return true;
+ }
+ *****************/
+ // ...for now just check cacheFlag
+ if (cacheFlag)
+ dataType = BUFFERED_AUDIO_DATA;
+ else
+ dataType = STREAMING_AUDIO_DATA;
+
+ if ((url == null) && (inputStream == null) && (path == null)) {
+ if (debugFlag)
+ debugPrint("JavaSoundMixer.loadSound null data - return error");
+ return true;
+ }
+
+ // get ais
+ if (path != null) {
+ // generate url from string, and pass url to driver
+ if (debugFlag) {
+ debugPrint("JavaSoundMixer.loadSound with path = " + path);
+ }
+ try {
+ url = new URL(path);
+ }
+ catch (Exception e) {
+ // do not throw an exception while rendering
+ return true;
+ }
+ }
+
+ // get DataLine channel based on data type
+ if (dataType == BUFFERED_AUDIO_DATA) {
+ if (debugFlag)
+ debugPrintln("JSSample.load dataType = BUFFERED ");
+ channel = new JSClip();
+ if (debugFlag)
+ debugPrintln(" calls JSClip.initAudioInputStream");
+ if (url != null)
+ ais = channel.initAudioInputStream(url, cacheFlag);
+ else if (inputStream != null)
+ ais = channel.initAudioInputStream(inputStream, cacheFlag);
+ if (ais == null) {
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer.prepareSound " +
+ "initAudioInputStream() failed");
+ return true;
+ }
+ if (debugFlag)
+ debugPrintln(" calls JSClip.initDataLine");
+ dataLine = channel.initDataLine(ais);
+ }
+ else if (dataType == STREAMING_AUDIO_DATA) {
+ if (debugFlag)
+ debugPrintln("JSSample.load dataType = STREAMING ");
+ channel = new JSStream();
+ if (debugFlag)
+ debugPrintln(" calls JSStream.initAudioInputStream");
+ if (url != null)
+ ais = channel.initAudioInputStream(url, cacheFlag);
+ else if (inputStream != null)
+ ais = channel.initAudioInputStream(inputStream, cacheFlag);
+ if (ais == null) {
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer.prepareSound " +
+ "initAudioInputStream() failed");
+ return true;
+ }
+ if (debugFlag)
+ debugPrintln(" calls JSStream.initDataLine");
+ dataLine = channel.initDataLine(ais);
+ }
+ else {
+ if (debugFlag)
+ debugPrintln("JSSample.load doesn't support MIDI yet");
+ }
+ if (dataLine == null) {
+ if (debugFlag)
+ debugPrint("JSSample.load initDataLine failed ");
+ channel = null;
+ return true;
+ }
+ duration = channel.getDuration();
+ if (debugFlag)
+ debugPrint("JSSample.load channel duration = " + duration);
+ /*
+ * Since no error occurred while loading, save all the characteristics
+ * for the sound in the sample.
+ */
+ setDirtyFlags(0xFFFF);
+ setSoundType(soundType);
+ setSoundData(soundData);
+
+ if (debugFlag)
+ debugPrintln("JSSample.load returned without error");
+ return false;
+ }
+
+ void reset() {
+ if (debugFlag)
+ debugPrint("JSSample.reset() exit");
+ rateRatio = 1.0f;
+ }
+
+// TODO: NEED methods for any field accessed by both JSThread and
+// JavaSoundMixer so that we can make these MT safe??
+ /*
+ * Process request for Filtering fields
+ */
+ boolean getFilterFlag() {
+ return false;
+ }
+ float getFilterFreq() {
+ return -1.0f;
+ }
+
+ void setCurrentRateRatio(float ratio) {
+ currentRateRatio = ratio;
+ }
+
+ float getCurrentRateRatio() {
+ return currentRateRatio;
+ }
+
+ void setTargetRateRatio(float ratio) {
+ targetRateRatio = ratio;
+ }
+
+ float getTargetRateRatio() {
+ return targetRateRatio;
+ }
+
+ void setRampRateFlag(boolean flag) {
+ rampRateFlag = flag;
+ }
+
+ boolean getRampRateFlag() {
+ return rampRateFlag;
+ }
+
+ void setDataType(int type) {
+ dataType = type;
+ }
+
+ int getDataType() {
+ return dataType;
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/audioengines/javasound/JSStream.java b/src/classes/share/com/sun/j3d/audioengines/javasound/JSStream.java
new file mode 100755
index 0000000..340e1af
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/audioengines/javasound/JSStream.java
@@ -0,0 +1,67 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.audioengines.javasound;
+
+/**
+ * The JSStream class defines audio output methods that call the JavaSound
+ * API methods for streams.
+ *
+ * <p>
+ * NOTE: This class is not yet implemented.
+ */
+
+class JSStream extends JSChannel {
+ private static boolean warningReported = false;
+
+ JSStream() {
+ // Report a "not implemented" warning message
+ if (!warningReported) {
+ System.err.println("***");
+ System.err.println("*** WARNING: JavaSoundMixer: Streaming (uncached) audio not implemented");
+ System.err.println("***");
+ warningReported = true;
+ }
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/audioengines/javasound/JSThread.java b/src/classes/share/com/sun/j3d/audioengines/javasound/JSThread.java
new file mode 100755
index 0000000..ed91910
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/audioengines/javasound/JSThread.java
@@ -0,0 +1,855 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.audioengines.javasound;
+
+/*
+ * JavaSound engine Thread
+ *
+ * IMPLEMENTATION NOTE: The JavaSoundMixer is incomplete and really needs
+ * to be rewritten. When this is done, we may or may not need this class.
+ */
+
+import javax.media.j3d.*;
+import com.sun.j3d.audioengines.*;
+
+/**
+ * The Thread Class extended for JavaSound Mixer specific audio device
+ * calls that dynamically, in 'real-time" change engine parameters
+ * such as volume/gain and sample-rate/frequency(pitch).
+ */
+
+class JSThread extends com.sun.j3d.audioengines.AudioEngineThread {
+
+ /**
+ * The thread data for this thread
+ */
+ int totalChannels = 0;
+ /**
+ * flags denoting if dynamic gain or rate interpolation is to be performed
+ */
+ boolean rampGain = false;
+
+ // global thread flat rampRate set true only when setTargetRate called
+ // for any sample. but it is cleared only by doWork when no sample
+ // has a need for the rate to be ramped any further.
+ boolean rampRate = false;
+
+/*** TODO:
+ *
+ * scalefactors applied to current sample rate to determine delta changes
+ * in rate (in Hz)
+ *
+ float currentGain = 1.0f;
+ float targetGain = 1.0f;
+***********/
+
+ // reference to engine that created this thread
+ AudioEngine3D audioEngine = null;
+
+ /**
+ * This constructor simply assigns the given id.
+ */
+ JSThread(ThreadGroup t, AudioEngine3DL2 engine) {
+ super(t, "J3D-JavaSoundThread");
+ audioEngine = engine;
+ // TODO: really get total JavaSound channels
+ totalChannels = 32;
+ if (debugFlag)
+ debugPrint("JSThread.constructor("+t+")");
+ }
+
+
+
+ /**
+ * This method performs one iteration of pending work to do
+ *
+ * Wildly "garbled" sounds was caused by unequal changes in delta
+ * time verses delta distances (resulting in jumps in rate factors
+ * calculated for Doppler. This work thread is meant to smoothly
+ * increment/decrement changes in rate (and other future parameters)
+ * until the target value is reached.
+ */
+ synchronized public void doWork() {
+ if (debugFlag)
+ debugPrint("JSThread.doWork()");
+/*******
+ while (rampRate || rampGain) {
+*********/
+/****** DESIGN
+// Loop while sound is playing, reget attributes and gains/reverb,... params
+// update lowlevel params then read modify then copy to line(s)
+
+can keep my own loop count for streams??? not really
+
+*******/
+ // QUESTION: will size ever get smaller after get performed???
+ int numSamples = audioEngine.getSampleListSize();
+ JSSample sample = null;
+ int numRateRamps = 0;
+ for (int index = 0; index < numSamples; index++) {
+ // loop thru samples looking for ones needing rate incremented
+ sample = (JSSample)audioEngine.getSample(index);
+ if (sample == null)
+ continue;
+ if (sample.getRampRateFlag()) {
+ if (debugFlag)
+ debugPrint(" rampRate true");
+ boolean endOfRampReached = adjustRate(sample);
+ sample.setRampRateFlag(!endOfRampReached);
+ if (!endOfRampReached)
+ numRateRamps++;
+ }
+ // TODO: support changes in gain this way as well
+ }
+ if (numRateRamps > 0) {
+ rampRate = true;
+runMonitor(RUN, 0, null);
+ }
+ else
+ rampRate = false;
+/*********
+ try {
+ Thread.sleep(4);
+ } catch (InterruptedException e){}
+*********/
+/********
+ } // while
+*********/
+ // otherwise do nothing
+ }
+
+ int getTotalChannels() {
+ return (totalChannels);
+ }
+
+ /**
+ * Gradually change rate scale factor
+ *
+ * If the rate change is too great suddenly, it sounds like a
+ * jump, so we need to change gradually over time.
+ * Since an octive delta change up is 2.0 but down is 0.5, forced
+ * "max" rate of change is different for both.
+ * @return true if target rate value was reached
+ */
+ boolean adjustRate(JSSample sample) {
+ // QUESTION: what should max delta rate changes be
+ // Using 1/32 of a half-step (1/12 of an octive)???
+ double maxRateChangeDown = 0.00130213;
+ double maxRateChangeUp = 0.00260417;
+
+ double lastActualRateRatio = sample.getCurrentRateRatio();
+ double requestedRateRatio = sample.getTargetRateRatio();
+ boolean endOfRamp = false; // flag denotes if target rate reached
+ if ( lastActualRateRatio > 0 ) {
+ double sampleRateRatio = requestedRateRatio; // in case diff = 0
+ double diff = 0.0;
+ if (debugFlag) {
+ debugPrint("JSThread.adjustRate: between " +
+ lastActualRateRatio + " & " + requestedRateRatio);
+ }
+ diff = requestedRateRatio - lastActualRateRatio;
+ if (diff > 0.0) { // direction of movement is towards listener
+ // inch up towards the requested target rateRatio
+ if (diff >= maxRateChangeUp) {
+ sampleRateRatio = lastActualRateRatio + maxRateChangeUp;
+ if (debugFlag) {
+ debugPrint(" adjustRate: " +
+ "diff >= maxRateChangeUp so ");
+ debugPrint(" adjustRate: " +
+ " sampleRateRatio incremented up by max");
+ }
+ endOfRamp = false; // target value not reached
+ }
+ /*
+ * otherwise delta change is within tolerance
+ * so use requested RateRatio as calculated w/out change
+ */
+ else {
+ sampleRateRatio = requestedRateRatio;
+ if (debugFlag) {
+ debugPrint(" adjustRate: " +
+ " requestedRateRatio reached");
+ }
+ endOfRamp = true; // reached
+ }
+ }
+ else if (diff < 0.0) { // movement is away from listener
+ // inch down towards the requested target rateRatio
+ if ((-diff) >= maxRateChangeDown) {
+ sampleRateRatio = lastActualRateRatio - maxRateChangeDown;
+ if (debugFlag) {
+ debugPrint(" adjustRate: " +
+ "-(diff) >= maxRateChangeUp so ");
+ debugPrint(" adjustRate: " +
+ " sampleRateRatio incremented down by max ");
+ }
+ endOfRamp = false; // target value not reached
+ }
+ /*
+ * otherwise negitive delta change is within tolerance so
+ * use sampleRateRatio as calculated w/out change
+ */
+ else {
+ sampleRateRatio = requestedRateRatio;
+ if (debugFlag) {
+ debugPrint(" adjustRate: " +
+ " requestedRateRatio reached");
+ }
+ endOfRamp = true; // reached
+ }
+ }
+ else // there is no difference between last set and requested rates
+ return true;
+
+ this.setSampleRate(sample, (float)sampleRateRatio);
+ }
+ else {
+ // this is the first time thru with a rate change
+ if (debugFlag) {
+ debugPrint(" adjustRate: " +
+ "last requested rateRatio not set yet " +
+ "so sampleRateRatio left unchanged");
+ }
+ this.setSampleRate(sample, (float)requestedRateRatio);
+ endOfRamp = false; // target value not reached
+ }
+ return endOfRamp;
+ } // adjustRate
+
+ void setSampleRate(JSSample sample, JSAuralParameters attribs) {
+// TODO:
+ }
+
+ // gain set at start sample time as well
+ void setSampleGain(JSSample sample, JSAuralParameters attribs) {
+/*******
+ // take fields as already set in sample and updates gain
+ // called after sample.render performed
+ if (debugFlag)
+ debugPrint("JSThread.setSampleGain()");
+leftGain, rightGain
+ if (debugFlag) {
+ debugPrint(" " +
+ "StereoGain during update " + leftGain +
+ ", " + rightGain);
+ debugPrint(" " +
+ "StereoDelay during update " + leftDelay +
+ ", " + rightDelay);
+ }
+ int dataType = sample.getDataType();
+ int soundType = sample.getSoundType();
+ boolean muted = sample.getMuteFlag();
+
+ if (debugFlag)
+ debugPrint("setStereoGain for sample "+sample+" " + leftGain +
+ ", " + rightGain);
+ if (dataType == JSAuralParameters.STREAMING_AUDIO_DATA ||
+ dataType == JSAuralParameters.BUFFERED_AUDIO_DATA ) {
+ thread.setSampleGain(sample, leftGain);
+ if ( (soundType == AudioDevice3D.CONE_SOUND) ||
+ (soundType == AudioDevice3D.POINT_SOUND) ) {
+ thread.setSampleGain(
+ ((JSPositionalSample)sample).getSecondIndex(), rightGain); thread.setSampleGain(
+ ((JSPositionalSample)sample).getReverbIndex(), reverbGain);
+ }
+ }
+ // TODO: JavaSound does not support MIDI song panning yet
+ else if (dataType == JSAuralParameters.STREAMING_MIDI_DATA ||
+
+ dataType == JSAuralParameters.BUFFERED_MIDI_DATA) {
+ // Stereo samples not used for Midi Song playback
+ thread.setSampleGain(sample, (leftGain+rightGain) );
+ ******
+ // -1.0 far left, 0.0 center, 1.0 far right
+ position = (leftGain - rightGain) / (leftGain + rightGain);
+ JSMidi.setSamplePan(sample, position);
+
+ if ( (soundType == AudioDevice3D.CONE_SOUND) ||
+ (soundType == AudioDevice3D.POINT_SOUND) ) {
+ JSMidi.setSampleGain(
+ ((JSPositionalSample)sample).getSecondIndex(), rightGain); JSMidi.setSampleGain(
+ ((JSPositionalSample)sample).getReverbIndex(), reverbGain);
+ }
+ ******
+ }
+ else {
+ if (debugFlag)
+ debugPrint( "JSThread: Internal Error setSampleGain dataType " +
+ dataType + " invalid");
+ return;
+ }
+ *****
+ // force specific gain
+ // go ahead and set gain immediately
+ this.setSampleGain(sample, scaleFactor);
+ rampGain = false; // disable ramping of gain
+******/
+ }
+
+ void setSampleDelay(JSSample sample, JSAuralParameters attribs) {
+/******
+ // take fields as already set in sample and updates delay
+ // called after sample.render performed
+ // adjust by attrib rolloff
+ float delayTime = attribs.reverbDelay * attribs.rolloff;
+
+ leftDelay = (int)(sample.leftDelay * attribs.rolloff);
+ rightDelay = (int)(sample.rightDelay * attribs.rolloff);
+leftDelay, rightDelay
+ int dataType = sample.getDataType();
+ int soundType = sample.getSoundType();
+ if (debugFlag)
+ debugPrint("setStereoDelay for sample "+sample+" " + leftDelay +
+ ", " + rightDelay);
+ if (dataType == JSAuralParameters.STREAMING_AUDIO_DATA) {
+ if ( (soundType == AudioDevice3D.CONE_SOUND) ||
+ (soundType == AudioDevice3D.POINT_SOUND) ) {
+ JSStream.setSampleDelay(
+ sample, leftDelay);
+ JSStream.setSampleDelay(
+ ((JSPositionalSample)sample).getSecondIndex(), rightDelay);
+ }
+ else
+ JSStream.setSampleDelay(sample, 0);
+ }
+ else if (dataType == JSAuralParameters.BUFFERED_AUDIO_DATA) {
+ if ( (soundType == AudioDevice3D.CONE_SOUND) ||
+ (soundType == AudioDevice3D.POINT_SOUND) ) {
+ JSClip.setSampleDelay(
+ sample, leftDelay);
+ JSClip.setSampleDelay(
+ ((JSPositionalSample)sample).getSecondIndex(), rightDelay);
+ }
+ else
+ JSClip.setSampleDelay(sample, 0);
+ }
+ else if (dataType == JSAuralParameters.STREAMING_MIDI_DATA ||
+
+ dataType == JSAuralParameters.BUFFERED_MIDI_DATA) {
+ ********
+ if ( (soundType == AudioDevice3D.CONE_SOUND) ||
+ (soundType == AudioDevice3D.POINT_SOUND) ) {
+ JSMidi.setSampleDelay(
+ sample, leftDelay);
+ JSMidi.setSampleDelay(
+ ((JSPositionalSample)sample).getSecondIndex(), rightDelay);
+ }
+ else
+ ********
+ JSMidi.setSampleDelay(sample, 0);
+ }
+ else {
+ if (debugFlag)
+ debugPrint( "JSThread: Internal Error setSampleDelay dataType " +
+ dataType + " invalid");
+ return;
+ }
+******/
+ }
+
+ void setTargetGain(JSSample sample, float scaleFactor) {
+/**********
+// TODO: implement this
+ // current gain is used as starting scalefactor for ramp
+// TEMPORARY: for now just set gain
+ this.setSampleGain(sample, scaleFactor);
+ rampGain = false;
+ rampGain = true;
+ targetGain = scaleFactor;
+ runMonitor(RUN, 0, null);
+**********/
+ }
+
+ void setRate(JSSample sample, float rateScaleFactor) {
+ // force specific rate
+ // go ahead and set rate immediately
+ // take fields as already set in sample and updates rate
+ // called after sample.render performed
+ this.setSampleRate(sample, rateScaleFactor);
+ // disables rate from being gradually increased or decreased
+ // don't set global thread flat rampRate false just because
+ // one sample's rate is set to a specific value.
+ sample.setRampRateFlag(false);
+ }
+
+ void setTargetRate(JSSample sample, float rateScaleFactor) {
+ // make gradual change in rate factors up or down to target rate
+ sample.setRampRateFlag(true);
+ sample.setTargetRateRatio(rateScaleFactor);
+ rampRate = true;
+ runMonitor(RUN, 0, null);
+ }
+
+// TODO: should have methods for delay and pan as well
+
+ void setSampleGain(JSSample sample, float gain) {
+/***********
+// QUESTION: What needs to be synchronized???
+ if (debugFlag)
+ debugPrint("JSThread.setSampleGain for sample "+sample+" " + gain );
+ int dataType = sample.getDataType();
+ int soundType = sample.getSoundType();
+ boolean muted = sample.getMuteFlag();
+// TODO:
+ if (dataType == JSAuralParameters.STREAMING_AUDIO_DATA)
+{
+ com.sun.j3d.audio.J3DHaeStream.setSampleGain(index, gain);
+ }
+ else if (dataType == JSAuralParameters.BUFFERED_AUDIO_DATA) {
+ com.sun.j3d.audio.J3DHaeClip.setSampleGain(index, gain);
+ }
+ else {
+ // dataType==JSAuralParameters.STREAMING_MIDI_DATA
+ // dataType==JSAuralParameters.BUFFERED_MIDI_DATA
+ com.sun.j3d.audio.J3DHaeMidi.setSampleGain(index, gain);
+ }
+***************/
+ }
+
+ void setSampleRate(JSSample sample, float scaleFactor) {
+/*********
+// QUESTION: What needs to be synchronized???
+ // TODO: use sample.rateRatio??
+ if (debugFlag)
+ debugPrint("JSThread.setSampleRate sample " +
+ sample + ", scale factor = " + scaleFactor);
+ int dataType = sample.getDataType();
+ int soundType = sample.getSoundType();
+
+// TODO:
+ if (dataType == JSAuralParameters.STREAMING_AUDIO_DATA) {
+ com.sun.j3d.audio.J3DHaeStream.scaleSampleRate(index, scaleFactor);
+ if ( (soundType == AudioDevice3D.CONE_SOUND) ||
+ (soundType == AudioDevice3D.POINT_SOUND) ) {
+ com.sun.j3d.audio.J3DHaeStream.scaleSampleRate(
+ ((JSPositionalSample)sample).getSecondIndex(),
+ scaleFactor);
+ com.sun.j3d.audio.J3DHaeStream.scaleSampleRate(
+ ((JSPositionalSample)sample).getReverbIndex(),
+ scaleFactor);
+ }
+ }
+ else if (dataType == JSAuralParameters.BUFFERED_AUDIO_DATA) {
+ com.sun.j3d.audio.J3DHaeClip.scaleSampleRate(index, scaleFactor);
+ if ( (soundType == AudioDevice3D.CONE_SOUND) ||
+ (soundType == AudioDevice3D.POINT_SOUND) ) {
+ com.sun.j3d.audio.J3DHaeClip.scaleSampleRate(
+ ((JSPositionalSample)sample).getSecondIndex(),
+ scaleFactor);
+ com.sun.j3d.audio.J3DHaeClip.scaleSampleRate(
+ ((JSPositionalSample)sample).getReverbIndex(),
+ scaleFactor);
+ }
+ }
+ else if (dataType == JSAuralParameters.STREAMING_MIDI_DATA ||
+ dataType == JSAuralParameters.BUFFERED_MIDI_DATA) {
+ com.sun.j3d.audio.J3DHaeMidi.scaleSampleRate(index, scaleFactor);
+ // TODO: MIDI only supported for Background sounds
+ }
+***********/
+ sample.setCurrentRateRatio(scaleFactor);
+ }
+
+ boolean startSample(JSSample sample) {
+/**********
+// QUESTION: should this have a return values - error - or not??
+
+ int returnValue = 0;
+ AuralParameters attribs = audioEngine.getAuralParameters();
+ int soundType = sample.getSoundType();
+ boolean muted = sample.getMuteFlag();
+ int dataType = sample.getDataType();
+ int loopCount = sample.getLoopCount();
+ float leftGain = sample.leftGain;
+ float rightGain = sample.rightGain;
+ int leftDelay = (int)(sample.leftDelay * attribs.rolloff);
+ int rightDelay = (int)(sample.rightDelay * attribs.rolloff);
+ if (dataType == JSAuralParameters.STREAMING_AUDIO_DATA) {
+ if (soundType == AudioDevice3D.BACKGROUND_SOUND) {
+ returnValue = JSStream.startSample(sample,
+ loopCount, leftGain);
+ if (debugFlag)
+ debugPrint("JSThread " +
+ "start stream backgroundSound with gain " + leftGain);
+ }
+ else { // soundType is POINT_SOUND or CONE_SOUND
+ // start up main left and right channels for spatial rendered sound
+ returnValue = JSStream.startSamples(sample,
+ ((JSPositionalSample)sample).getSecondIndex(),
+ loopCount, leftGain, rightGain, leftDelay, rightDelay);
+ //
+ // start up reverb channel w/out delay even if reverb not on now //
+ float reverbGain = 0.0f;
+ if (!muted && auralParams.reverbFlag) {
+ reverbGain = sample.getGain() *
+ attribs.reflectionCoefficient;
+ }
+ int reverbRtrnVal = JSStream.startSample(
+ ((JSPositionalSample)sample).getReverbIndex(), loopCount, reverbGain);
+ if (debugFlag)
+ debugPrint("JSThread " +
+ "start stream positionalSound with gain "+ leftGain +
+ ", " + rightGain);
+ }
+ }
+
+ else if (dataType == JSAuralParameters.BUFFERED_AUDIO_DATA) {
+ if (soundType == AudioDevice3D.BACKGROUND_SOUND) {
+ returnValue = JSClip.startSample(sample,
+ loopCount, leftGain );
+ if (debugFlag)
+ debugPrint("JSThread " +
+ "start buffer backgroundSound with gain " + leftGain);
+ }
+ else { // soundType is POINT_SOUND or CONE_SOUND
+ // start up main left and right channels for spatial rendered sound
+ returnValue = JSClip.startSamples(sample,
+ ((JSPositionalSample)sample).getSecondIndex(),
+ loopCount, leftGain, rightGain, leftDelay, rightDelay);
+ //
+ // start up reverb channel w/out delay even if reverb not on now //
+ float reverbGain = 0.0f;
+ if (!muted && auralParams.reverbFlag) {
+ reverbGain = sample.getGain() *
+ attribs.reflectionCoefficient;
+ }
+ int reverbRtrnVal = JSClip.startSample(
+ ((JSPositionalSample)sample).getReverbIndex(),
+ loopCount, reverbGain);
+
+ if (debugFlag)
+ debugPrint("JSThread " +
+ "start stream positionalSound with gain " + leftGain
+ + ", " + rightGain);
+ }
+ }
+ else if (dataType == JSAuralParameters.STREAMING_MIDI_DATA ||
+ dataType == JSAuralParameters.BUFFERED_MIDI_DATA) {
+ if (soundType == AudioDevice3D.BACKGROUND_SOUND) {
+ returnValue = JSMidi.startSample(sample,
+ loopCount, leftGain);
+ if (debugFlag)
+ debugPrint("JSThread " +
+ "start Midi backgroundSound with gain " + leftGain);
+ }
+ else { // soundType is POINT_SOUND or CONE_SOUND
+ // start up main left and right channels for spatial rendered sound
+ returnValue = JSMidi.startSamples(sample,
+ ((JSPositionalSample)sample).getSecondIndex(),
+ loopCount, leftGain, rightGain, leftDelay, rightDelay);
+ *******
+ // TODO: positional MIDI sounds not supported.
+ // The above startSamples really just start on sample
+ // Don't bother with reverb channel for now.
+
+ //
+ // start up reverb channel w/out delay even if reverb not on now //
+ float reverbGain = 0.0f;
+ if (!muted && auralParams.reverbFlag) {
+ reverbGain = sample.getGain() *
+ attribs.reflectionCoefficient;
+ }
+ int reverbRtrnVal = JSMidi.startSample(
+ ((JSPositionalSample)sample).getReverbIndex(), loopCount, reverbGain);
+ *******
+ if (debugFlag)
+ debugPrint("JSThread " +
+ "start Midi positionalSound with gain "+ leftGain +
+ ", " + rightGain);
+ }
+ }
+
+ else {
+ if (debugFlag)
+ debugPrint(
+ "JSThread: Internal Error startSample dataType " +
+ dataType + " invalid");
+ return false;
+ }
+ // TODO: have to look at return values and conditionally return 'success'
+**********/
+ return true;
+ }
+
+ boolean stopSample(JSSample sample) {
+/***********
+// QUESTION: should this have a return values - error - or not??
+ int dataType = sample.getDataType();
+ int soundType = sample.getSoundType();
+
+ int returnValue = 0;
+ if (dataType == JSAuralParameters.STREAMING_AUDIO_DATA) {
+ if ( (soundType == AudioDevice3D.CONE_SOUND) ||
+ (soundType == AudioDevice3D.POINT_SOUND) ) {
+ returnValue = JSStream.stopSamples(sample,
+ ((JSPositionalSample)sample).getSecondIndex());
+ returnValue = JSStream.stopSample(
+ ((JSPositionalSample)sample).getReverbIndex());
+ }
+ else
+ returnValue = JSStream.stopSample(sample);
+ }
+ else if (dataType == JSAuralParameters.BUFFERED_AUDIO_DATA) {
+ if ( (soundType == AudioDevice3D.CONE_SOUND) ||
+ (soundType == AudioDevice3D.POINT_SOUND) ) {
+ returnValue = JSClip.stopSamples(sample,
+ ((JSPositionalSample)sample).getSecondIndex());
+ returnValue = JSClip.stopSample(
+ ((JSPositionalSample)sample).getReverbIndex());
+ }
+ else
+ returnValue = JSClip.stopSample(sample);
+ }
+ else if (dataType == JSAuralParameters.STREAMING_MIDI_DATA ||
+ dataType == JSAuralParameters.BUFFERED_MIDI_DATA) {
+
+ *****
+ // TODO: positional sounds NOT supported yet
+ if ( (soundType == AudioDevice3D.CONE_SOUND) ||
+ (soundType == AudioDevice3D.POINT_SOUND) ) {
+ returnValue = JSMidi.stopSamples(sample,
+ ((JSPositionalSample)sample).getSecondIndex());
+ returnValue = JSMidi.stopSample(
+ ((JSPositionalSample)sample).getReverbIndex());
+ }
+ else
+ *****
+ returnValue = JSMidi.stopSample(sample);
+ }
+ else {
+ if (debugFlag)
+ debugPrint( "JSThread: Internal Error stopSample dataType " +
+ dataType + " invalid");
+ return -1;
+ }
+
+************/
+ return true;
+ }
+
+
+ void pauseSample(JSSample sample) {
+/**********
+ int dataType = sample.getDataType();
+ int soundType = sample.getSoundType();
+ int returnValue = 0;
+ if (dataType == JSAuralParameters.STREAMING_AUDIO_DATA) {
+ if ( (soundType == AudioDevice3D.CONE_SOUND) ||
+ (soundType == AudioDevice3D.POINT_SOUND) ) {
+ returnValue = JSStream.pauseSamples(sample,
+ ((JSPositionalSample)sample).getSecondIndex());
+ returnValue = JSStream.pauseSample(
+ ((JSPositionalSample)sample).getReverbIndex());
+ }
+ else
+ returnValue = JSStream.pauseSample(sample);
+ }
+ else if (dataType == JSAuralParameters.BUFFERED_AUDIO_DATA) {
+ if ( (soundType == AudioDevice3D.CONE_SOUND) ||
+ (soundType == AudioDevice3D.POINT_SOUND) ) {
+ returnValue = JSClip.pauseSamples(sample,
+ ((JSPositionalSample)sample).getSecondIndex());
+ returnValue = JSClip.pauseSample(
+ ((JSPositionalSample)sample).getReverbIndex());
+ }
+ else
+ returnValue = JSClip.pauseSample(sample);
+ }
+ else if (dataType == JSAuralParameters.STREAMING_MIDI_DATA ||
+
+ dataType == JSAuralParameters.BUFFERED_MIDI_DATA) {
+ *******
+ if ( (soundType == AudioDevice3D.CONE_SOUND) ||
+ (soundType == AudioDevice3D.POINT_SOUND) ) {
+ returnValue = JSMidi.pauseSamples(sample,
+ ((JSPositionalSample)sample).getSecondIndex());
+ returnValue = JSMidi.pauseSample(
+ ((JSPositionalSample)sample).getReverbIndex());
+ }
+ else
+ *****
+ returnValue = JSMidi.pauseSample(sample);
+ }
+ else {
+ if (debugFlag)
+ debugPrint(
+ "JSThread: Internal Error pauseSample dataType " +
+ dataType + " invalid");
+ }
+ if (returnValue < 0) {
+ if (debugFlag)
+ debugPrint( "JSThread: Internal Error pauseSample " +
+ "for sample " + sample + " failed");
+ }
+// QUESTION: return value or not???
+ return;
+*************/
+ }
+
+ void unpauseSample(JSSample sample) {
+/**************
+ int dataType = sample.getDataType();
+ int soundType = sample.getSoundType();
+ int returnValue = 0;
+ if (dataType == JSAuralParameters.STREAMING_AUDIO_DATA) {
+ if ( (soundType == AudioDevice3D.CONE_SOUND) ||
+ (soundType == AudioDevice3D.POINT_SOUND) ) {
+ returnValue = JSStream.unpauseSamples(sample,
+ ((JSPositionalSample)sample).getSecondIndex());
+ returnValue = JSStream.unpauseSample(
+ ((JSPositionalSample)sample).getReverbIndex());
+ }
+ else
+ returnValue = JSStream.unpauseSample(sample);
+ }
+ else if (dataType == JSAuralParameters.BUFFERED_AUDIO_DATA) {
+ if ( (soundType == AudioDevice3D.CONE_SOUND) ||
+ (soundType == AudioDevice3D.POINT_SOUND) ) {
+ returnValue = JSClip.unpauseSamples(sample,
+ ((JSPositionalSample)sample).getSecondIndex());
+ returnValue = JSClip.unpauseSample(
+ ((JSPositionalSample)sample).getReverbIndex());
+ }
+ else
+ returnValue = JSClip.unpauseSample(sample);
+ }
+ else if (dataType == JSAuralParameters.STREAMING_MIDI_DATA ||
+
+ dataType == JSAuralParameters.BUFFERED_MIDI_DATA) {
+ *********
+ // TODO: positional Midi sounds
+ if ( (soundType == AudioDevice3D.CONE_SOUND) ||
+ (soundType == AudioDevice3D.POINT_SOUND) ) {
+ returnValue = JSMidi.unpauseSamples(sample,
+ ((JSPositionalSample)sample).getSecondIndex());
+ returnValue = JSMidi.unpauseSample(
+ ((JSPositionalSample)sample).getReverbIndex());
+ }
+ else
+ *********
+ returnValue = JSMidi.unpauseSample(sample);
+ }
+ else {
+ if (debugFlag)
+ debugPrint(
+ "JSThread: Internal Error unpauseSample dataType " + dataType + " invalid");
+ }
+ if (returnValue < 0) {
+ if (debugFlag)
+ debugPrint( "JSThread: Internal Error unpauseSample " +
+ "for sample " + sample + " failed");
+
+ }
+// QUESTION: return value or not???
+ return;
+*************/
+ }
+
+// TODO:
+ void muteSample(JSSample sample) {
+ // is this already muted? if so don't do anytning
+
+ // This determines if mute is done as a zero gain or
+ // as a stop, advance restart...
+ }
+
+// TODO:
+ void unmuteSample(JSSample sample) {
+ if (debugFlag)
+ debugPrint( "JSThread.unmuteSample not implemented");
+ }
+
+ int startStreams() {
+// QUESTION: return value or not???
+ return 0;
+ }
+ int startStream() {
+// QUESTION: return value or not???
+ return 0;
+ }
+ int startClips() {
+// QUESTION: return value or not???
+ return 0;
+ }
+ int startClip() {
+// QUESTION: return value or not???
+ return 0;
+ }
+
+ /**
+ * This initializes this thread. Once this method returns, the thread is
+ * ready to do work.
+ */
+ public void initialize() {
+ super.initialize();
+ // this.setPriority(Thread.MAX_PRIORITY);
+ // TODO: init values of fields???
+ if (debugFlag)
+ debugPrint("JSThread.initialize()");
+ // TODO: doesn't do anything yet
+ }
+
+ /**
+ * Code to close the device
+ * @return flag: true is closed sucessfully, false if error
+ */
+ boolean close() {
+ // TODO: for now do nothing
+ return false;
+ }
+
+ public void shutdown() {
+ }
+
+
+
+
+ // default resource clean up method
+ public void cleanup() {
+ super.cleanup();
+ if (debugFlag)
+ debugPrint("JSThread.cleanup()");
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/audioengines/javasound/JavaSoundMixer.java b/src/classes/share/com/sun/j3d/audioengines/javasound/JavaSoundMixer.java
new file mode 100755
index 0000000..2d5bc73
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/audioengines/javasound/JavaSoundMixer.java
@@ -0,0 +1,959 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+/*
+ * Audio device driver using Java Sound Mixer Engine.
+ *
+ * IMPLEMENTATION NOTE: The JavaSoundMixer is incomplete and really needs
+ * to be rewritten.
+ */
+
+package com.sun.j3d.audioengines.javasound;
+
+import java.net.URL;
+import java.io.InputStream;
+import javax.vecmath.*;
+import javax.media.j3d.*;
+import com.sun.j3d.audioengines.*;
+import java.util.ArrayList;
+import java.lang.Thread;
+
+/**
+ * The JavaSoundMixer Class defines an audio output device that accesses
+ * JavaSound functionality stream data.
+ */
+public class JavaSoundMixer extends AudioEngine3DL2 {
+
+ // Debug print flags and methods
+ static final boolean debugFlag = false;
+ static final boolean internalErrors = false;
+
+ void debugPrint(String message) {
+ if (debugFlag)
+ System.out.println(message);
+ }
+
+ void debugPrintln(String message) {
+ if (debugFlag)
+ System.out.println(message);
+ }
+
+ // Determines method to call for added or setting sound into ArrayList
+ static final int ADD_TO_LIST = 1;
+ static final int SET_INTO_LIST = 2;
+
+ // current Aural Parameters = Aural Attributes from core + JavaSound
+ // specific fields, including reverberation parameters.
+ JSAuralParameters auralParams = null;
+
+ // thread for dynamically changing audio parameters such as volume
+ // and sample rate.
+ JSThread thread = null;
+
+ /*
+ * new fields in extended class
+ */
+ protected float deviceGain = 1.0f;
+
+ protected static final int NOT_PAUSED = 0;
+ protected static final int PAUSE_PENDING = 1;
+ protected static final int PAUSED = 2;
+ protected static final int RESUME_PENDING = 3;
+ protected int pause = NOT_PAUSED;
+
+ /*
+ * Construct a new JavaSoundMixer with the specified P.E.
+ * @param physicalEnvironment the physical environment object where we
+ * want access to this device.
+ */
+ public JavaSoundMixer(PhysicalEnvironment physicalEnvironment ) {
+ super(physicalEnvironment);
+ thread = new JSThread(Thread.currentThread().getThreadGroup(), this);
+ }
+
+ /**
+ * Query total number of channels available for sound rendering
+ * for this audio device.
+ * Overridden method from AudioEngine.
+ * @return number of maximum voices play simultaneously on JavaSound Mixer.
+ */
+ public int getTotalChannels() {
+ if (thread != null)
+ return thread.getTotalChannels();
+ else
+ return 32;
+ }
+
+ /**
+ * Code to initialize the device
+ * New interface to mixer/engine specific methods
+ * @return flag: true is initialized sucessfully, false if error
+ */
+ public boolean initialize() {
+ if (thread == null) {
+ return false;
+ }
+ // init JavaSound dynamic thread
+ thread.initialize();
+ auralParams = new JSAuralParameters();
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer: JSStream.initialize returned true");
+ return true;
+ }
+
+ /**
+ * Code to close the device.
+ * New interface to mixer/engine specific methods
+ * @return flag: true is closed sucessfully, false if error
+ */
+ public boolean close() {
+ if (thread == null)
+ return false;
+ if (thread.close()) {
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer: JSStream.close returned true");
+ return true;
+ }
+ else {
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer: JSStream.close returned false");
+ return false;
+ }
+ }
+
+
+ /**
+ * Code to load sound data into a channel of device mixer.
+ * Load sound as one or mores sample into the Java Sound Mixer:
+ * a) as either a STREAM or CLIP based on whether cached is enabled
+ * b) positional and directional sounds use three samples per
+ * sound
+ * Overriden method from AudioEngine3D.
+ *
+ * Sound type determines if this is a Background, Point or Cone
+ * sound source and thus the JSXxxxSample object type
+ * Call JSXxxxxSample.loadSample()
+ * If no error
+ * Get the next free index in the samples list.
+ * Store a reference to JSXxxxSample object in samples list.
+ * @return index to the sample in samples list.
+ */
+ public int prepareSound(int soundType, MediaContainer soundData) {
+ int index = JSSample.NULL_SAMPLE;
+ int methodType = ADD_TO_LIST;
+ if (soundData == null)
+ return JSSample.NULL_SAMPLE;
+ synchronized(samples) {
+ // for now force to just add to end of samples list
+ int samplesSize = samples.size();
+ index = samplesSize;
+ samples.ensureCapacity(index+1);
+ boolean error = false;
+
+ if (soundType == AudioDevice3D.CONE_SOUND) {
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer.prepareSound type=CONE");
+ JSDirectionalSample dirSample = new JSDirectionalSample();
+ error = dirSample.load(soundData);
+ if (error)
+ return JSSample.NULL_SAMPLE;
+ if (methodType == SET_INTO_LIST)
+ samples.set(index, dirSample);
+ else
+ samples.add(index, dirSample);
+ /*
+ * Since no error occurred while loading, save all the
+ * characterstics for the sound in the sample.
+ */
+ dirSample.setDirtyFlags(0xFFFF);
+ dirSample.setSoundType(soundType);
+ dirSample.setSoundData(soundData);
+
+ }
+ else if (soundType == AudioDevice3D.POINT_SOUND) {
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer.prepareSound type=POINT");
+ JSPositionalSample posSample = new JSPositionalSample();
+ error = posSample.load(soundData);
+ if (error)
+ return JSSample.NULL_SAMPLE;
+ if (methodType == SET_INTO_LIST)
+ samples.set(index, posSample);
+ else
+ samples.add(index, posSample);
+ posSample.setDirtyFlags(0xFFFF);
+ posSample.setSoundType(soundType);
+ posSample.setSoundData(soundData);
+ }
+ else { // soundType == AudioDevice3D.BACKGROUND_SOUND
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer.prepareSound type=BACKGROUND");
+ JSSample sample = null;
+ sample = new JSSample();
+ error = sample.load(soundData);
+ if (error)
+ return JSSample.NULL_SAMPLE;
+ if (methodType == SET_INTO_LIST)
+ samples.set(index, sample);
+ else
+ samples.add(index, sample);
+ sample.setDirtyFlags(0xFFFF);
+ sample.setSoundType(soundType);
+ sample.setSoundData(soundData);
+ }
+ }
+
+ if (debugFlag) {
+ debugPrint(" prepareSound type = "+soundType);
+ debugPrintln("JavaSoundMixer.prepareSound returned "+index);
+ }
+ return index;
+ }
+
+ /**
+ * Clears the fields associated with sample data for this sound.
+ * Overriden method from AudioEngine3D.
+ */
+ public void clearSound(int index) {
+ // TODO: call JSXXXX clear method
+ JSSample sample = null;
+ if ( (sample = (JSSample)getSample(index)) == null)
+ return;
+ sample.clear();
+ synchronized(samples) {
+ samples.set(index, null);
+ }
+ }
+
+ /**
+ * Save a reference to the local to virtual world coordinate space
+ * Overriden method from AudioEngine3D.
+ */
+ public void setVworldXfrm(int index, Transform3D trans) {
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer: setVworldXfrm for index " + index);
+ super.setVworldXfrm(index, trans);
+ if (debugFlag) {
+ double[] matrix = new double[16];
+ trans.get(matrix);
+ debugPrintln("JavaSoundMixer column-major transform ");
+ debugPrintln("JavaSoundMixer " + matrix[0]+", "+matrix[1]+
+ ", "+matrix[2]+", "+matrix[3]);
+ debugPrintln("JavaSoundMixer " + matrix[4]+", "+matrix[5]+
+ ", "+matrix[6]+", "+matrix[7]);
+ debugPrintln("JavaSoundMixer " + matrix[8]+", "+matrix[9]+
+ ", "+matrix[10]+", "+matrix[11]);
+ debugPrintln("JavaSoundMixer " + matrix[12]+", "+matrix[13]+
+ ", "+matrix[14]+", "+matrix[15]);
+ }
+ JSSample sample = null;
+ if ((sample = (JSSample)getSample(index)) == null)
+ return;
+ int soundType = sample.getSoundType();
+
+ if (soundType == AudioDevice3D.CONE_SOUND) {
+ JSDirectionalSample dirSample = null;
+ if ((dirSample = (JSDirectionalSample)getSample(index)) == null)
+ return;
+ dirSample.setXformedDirection();
+ dirSample.setXformedPosition();
+ // flag that VirtualWorld transform set
+ dirSample.setVWrldXfrmFlag(true);
+ }
+ else if (soundType == AudioDevice3D.POINT_SOUND) {
+ JSPositionalSample posSample = null;
+ if ((posSample = (JSPositionalSample)getSample(index)) == null)
+ return;
+ posSample.setXformedPosition();
+ // flag that VirtualWorld transform set
+ posSample.setVWrldXfrmFlag(true);
+ }
+ return;
+ }
+ /*
+ * Overriden method from AudioEngine3D.
+ */
+ public void setPosition(int index, Point3d position) {
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer: setPosition for index " + index);
+ super.setPosition(index, position);
+ JSPositionalSample posSample = null;
+ if ((posSample = (JSPositionalSample)getSample(index)) == null)
+ return;
+ int soundType = posSample.getSoundType();
+ if ( (soundType == AudioDevice3D.POINT_SOUND) ||
+ (soundType == AudioDevice3D.CONE_SOUND) ) {
+ posSample.setXformedPosition();
+ }
+ return;
+ }
+
+ /*
+ * Overriden method from AudioEngine3D.
+ */
+ public void setDirection(int index, Vector3d direction) {
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer: setDirection for index " + index);
+ super.setDirection(index, direction);
+ JSDirectionalSample dirSample = null;
+ if ((dirSample = (JSDirectionalSample)getSample(index)) == null)
+ return;
+ int soundType = dirSample.getSoundType();
+ if (soundType == AudioDevice3D.CONE_SOUND) {
+ dirSample.setXformedDirection();
+ }
+ return;
+ }
+
+ /*
+ * Overriden method from AudioEngine3D.
+ */
+ public void setReflectionCoefficient(float coefficient) {
+ super.setReflectionCoefficient(coefficient);
+ auralParams.reverbDirty |= JSAuralParameters.REFLECTION_COEFF_CHANGED;
+ return;
+ }
+
+ /*
+ * Overriden method from AudioEngine3D.
+ */
+ public void setReverbDelay(float reverbDelay) {
+ super.setReverbDelay(reverbDelay);
+ auralParams.reverbDirty |= JSAuralParameters.REVERB_DELAY_CHANGED;
+ return;
+ }
+
+ /*
+ * Overriden method from AudioEngine3D.
+ */
+ public void setReverbOrder(int reverbOrder) {
+ super.setReverbOrder(reverbOrder);
+ auralParams.reverbDirty |= JSAuralParameters.REVERB_ORDER_CHANGED;
+ return;
+ }
+
+ /*
+ * QUESTION: if this is used, for now, exclusively, to start a Background
+ * or any single sampled Sounds, why are there if-else cases to handle
+ * Point and Cone sounds??
+ *
+ * For now background sounds are not reverberated
+ *
+ * Overriden method from AudioEngine3D.
+ */
+ public int startSample(int index) {
+ // TODO: Rewrite this function
+
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer: STARTSample for index " + index);
+
+ JSSample sample = null;
+ if ( ( (sample = (JSSample)getSample(index)) == null) ||
+ thread == null )
+ return JSSample.NULL_SAMPLE;
+
+ int soundType = sample.getSoundType();
+ boolean muted = sample.getMuteFlag();
+ if (muted) {
+ if (debugFlag)
+ debugPrintln(" MUTEd start");
+ thread.muteSample(sample);
+ if (soundType != AudioDevice3D.BACKGROUND_SOUND)
+ setFilter(index, false, Sound.NO_FILTER);
+ }
+ else {
+ sample.render(sample.getDirtyFlags(), getView(), auralParams);
+ this.scaleSampleRate(index, sample.rateRatio);
+ // filtering
+ if (soundType != AudioDevice3D.BACKGROUND_SOUND)
+ setFilter(index, sample.getFilterFlag(), sample.getFilterFreq());
+ }
+
+ boolean startSuccessful;
+ startSuccessful = thread.startSample(sample);
+
+ sample.channel.startSample(sample.getLoopCount(), sample.getGain(), 0);
+
+ if (!startSuccessful) {
+ if (internalErrors)
+ debugPrintln(
+ "JavaSoundMixer: Internal Error startSample for index " +
+ index + " failed");
+ return JSSample.NULL_SAMPLE;
+ }
+ else {
+ if (debugFlag)
+ debugPrintln(" startSample worked, " +
+ "returning " + startSuccessful);
+ // NOTE: Set AuralParameters AFTER sound started
+ // Setting AuralParameters before you start sound doesn't work
+ if (!muted) {
+ if (auralParams.reverbDirty > 0) {
+ if (debugFlag) {
+ debugPrintln("startSample: reverb settings are:");
+ debugPrintln(" coeff = "+
+ auralParams.reflectionCoefficient +
+ ", delay = " + auralParams.reverbDelay +
+ ", order = " + auralParams.reverbOrder);
+ }
+ float delayTime = auralParams.reverbDelay * auralParams.rolloff;
+ calcReverb(sample);
+ }
+ // NOTE: it apprears that reverb has to be reset in
+ // JavaSound engine when sound re-started??
+ // force reset of reverb parameters when sound is started
+ setReverb(sample);
+ }
+ return index;
+ }
+ }
+
+ /*
+ * Overriden method from AudioEngine3D.
+ */
+ public int stopSample(int index) {
+ // TODO: Rewrite this function
+
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer: STOPSample for index " + index);
+ JSSample sample = null;
+ if ((sample = (JSSample)getSample(index)) == null)
+ return -1;
+
+ int dataType = sample.getDataType();
+ int soundType = sample.getSoundType();
+
+ boolean stopSuccessful = true;
+ stopSuccessful = thread.stopSample(sample);
+
+ sample.channel.stopSample();
+
+ if (!stopSuccessful) {
+ if (internalErrors)
+ debugPrintln( "JavaSoundMixer: Internal Error stopSample(s) for index " +
+ index + " failed");
+ return -1;
+ }
+ else {
+ // set fields in sample to reset for future start
+ sample.reset();
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer: stopSample for index " +
+ index + " worked, returning " + stopSuccessful);
+ return 0;
+ }
+ }
+
+ /*
+ * Overriden method from AudioEngine3D.
+ */
+ public void pauseSample(int index) {
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer: PAUSESample for index " + index);
+ JSSample sample = null;
+ if ((sample = (JSSample)getSample(index)) == null)
+ return;
+ // check thread != null
+ thread.pauseSample(sample);
+ }
+
+ /*
+ * Overriden method from AudioEngine3D.
+ */
+ public void unpauseSample(int index) {
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer: UNPAUSESample for index " + index);
+ JSSample sample = null;
+ if ((sample = (JSSample)getSample(index)) == null)
+ return;
+ thread.unpauseSample(sample);
+ }
+
+ /*
+ * Force thread to update sample.
+ * Overriden method from AudioEngine3D.
+ */
+
+ public void updateSample(int index) {
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer: UPDATESample for index " + index);
+ JSSample sample = null;
+ if ( ( (sample = (JSSample)getSample(index)) == null) ||
+ thread == null )
+ return;
+
+ int soundType = sample.getSoundType();
+ boolean muted = sample.getMuteFlag();
+
+ if (muted) {
+ if (soundType != AudioDevice3D.BACKGROUND_SOUND)
+ setFilter(index, false, Sound.NO_FILTER);
+ thread.muteSample(sample);
+ if (debugFlag)
+ debugPrintln(" Mute during update");
+ }
+ else {
+ // If reverb parameters changed resend to audio device
+ if (auralParams.reverbDirty > 0) {
+ if (debugFlag) {
+ debugPrintln("updateSample: reverb settings are:");
+ debugPrintln(" coeff = " + auralParams.reflectionCoefficient+
+ ", delay = " + auralParams.reverbDelay +
+ ", order = " + auralParams.reverbOrder);
+ }
+ float delayTime = auralParams.reverbDelay * auralParams.rolloff;
+ calcReverb(sample);
+ }
+ // TODO: Only re-set reverb if values different
+ // For now force reset to ensure that reverb is currently correct
+ setReverb(sample); // ensure reverb is current/correct
+
+ // TODO: For now sum left & rightGains for reverb gain
+ float reverbGain = 0.0f;
+ if (!muted && auralParams.reverbFlag) {
+ reverbGain = sample.getGain() * auralParams.reflectionCoefficient;
+ }
+
+ sample.render(sample.getDirtyFlags(), getView(), auralParams);
+
+ // filtering
+ if (soundType != AudioDevice3D.BACKGROUND_SOUND)
+ setFilter(index, sample.getFilterFlag(), sample.getFilterFreq());
+ thread.setSampleGain(sample, auralParams);
+ thread.setSampleRate(sample, auralParams);
+ thread.setSampleDelay(sample, auralParams);
+ }
+ return;
+ }
+
+ /*
+ * Overriden method from AudioEngine3D.
+ */
+ public void muteSample(int index) {
+ JSSample sample = null;
+ if ((sample = (JSSample)getSample(index)) == null)
+ return;
+
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer: muteSample");
+ sample.setMuteFlag(true);
+ thread.muteSample(sample);
+ return;
+ }
+
+ /*
+ * Overriden method from AudioEngine3D.
+ */
+ public void unmuteSample(int index) {
+ JSSample sample = null;
+ if ((sample = (JSSample)getSample(index)) == null)
+ return;
+
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer: unmuteSample");
+ sample.setMuteFlag(false);
+
+ // since while mute the reverb type and state was not updated...
+ // Reverb has to be recalculated when sound is unmuted .
+ auralParams.reverbDirty = 0xFFFF; // force an update of reverb params
+ sample.setDirtyFlags(0xFFFF); // heavy weight forcing of gain/delay update
+
+ // TODO: force an update of ALL parameters that could have changed
+ // while muting disabled...
+
+ thread.unmuteSample(sample);
+ return;
+ }
+
+ /*
+ * Overriden method from AudioEngine3D.
+ */
+ public long getSampleDuration(int index) {
+ JSSample sample = null;
+ if ((sample = (JSSample)getSample(index)) == null)
+ return Sample.DURATION_UNKNOWN;
+ long duration;
+
+ if (sample != null)
+ duration = sample.getDuration();
+ else
+ duration = Sample.DURATION_UNKNOWN;
+ if (debugFlag)
+ debugPrintln(" return duration " + duration);
+ return duration;
+ }
+
+ /*
+ * Overriden method from AudioEngine3D.
+ */
+ public int getNumberOfChannelsUsed(int index) {
+ /*
+ * Calls same method with different signature containing the
+ * sample's mute flag passed as the 2nd parameter.
+ */
+ JSSample sample = null;
+ if ((sample = (JSSample)getSample(index)) == null)
+ return 0;
+ else
+ return getNumberOfChannelsUsed(index, sample.getMuteFlag());
+ }
+
+ /**
+ * Overriden method from AudioEngine3D.
+ */
+ public int getNumberOfChannelsUsed(int index, boolean muted) {
+ /*
+ * The JavaSoundMixer implementation uses THREE channels to render
+ * the stereo image of each Point and Cone Sounds:
+ * Two for rendering the right and left portions of the rendered
+ * spatialized sound image - panned hard right or left respectively.
+ * This implementation uses one channel to render Background sounds
+ * whether the sample is mono or stereo.
+ *
+ * TODO: When muted is implemented, that flag should be check
+ * so that zero is returned.
+ */
+ JSSample sample = null;
+ if ((sample = (JSSample)getSample(index)) == null)
+ return 0;
+
+ int soundType = sample.getSoundType();
+ int dataType = sample.getDataType();
+
+ // TODO: for now positional Midi sound used only 1 sample
+ if (dataType == JSSample.STREAMING_MIDI_DATA ||
+ dataType == JSSample.BUFFERED_MIDI_DATA)
+ return 1;
+
+ if (soundType == BACKGROUND_SOUND)
+ return 1;
+ else // for Point and Cone sounds
+ return 3;
+ }
+
+ /*
+ * Overriden method from AudioEngine3D.
+ */
+ public long getStartTime(int index) {
+ JSSample sample = null;
+ if ((sample = (JSSample)getSample(index)) == null)
+ return 0L;
+ if (sample.channel == null)
+ return 0L;
+ return (long)sample.channel.startTime;
+ }
+
+ /*
+ * Methods called during rendering
+ */
+ void scaleSampleRate(int index, float scaleFactor) {
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer: scaleSampleRate index " +
+ index + ", scale factor = " + scaleFactor);
+ JSSample sample = null;
+ if ((sample = (JSSample)getSample(index)) == null ||
+ thread == null)
+ return;
+ int dataType = sample.getDataType();
+ if (debugFlag)
+ debugPrintln(" scaleSampleRate.dataType = " + dataType +
+ "using sample " + sample + " from samples[" +
+ index +"]");
+ int soundType = sample.getSoundType();
+
+ if (dataType == JSSample.STREAMING_AUDIO_DATA ||
+ dataType == JSSample.BUFFERED_AUDIO_DATA) {
+ thread.setSampleRate(sample, scaleFactor);
+ /**********
+ // TODO:
+ if (soundType != AudioDevice3D.BACKGROUND_SOUND) {
+ thread.setSampleRate( ((JSPositionalSample)sample).getSecondIndex(),
+ scaleFactor);
+ thread.setSampleRate(((JSPositionalSample)sample).getReverbIndex(),
+ scaleFactor);
+ }
+ **********/
+ }
+ else if (dataType == JSSample.STREAMING_MIDI_DATA ||
+ dataType == JSSample.BUFFERED_MIDI_DATA) {
+ thread.setSampleRate(sample, scaleFactor);
+ /**********
+ if (soundType != AudioDevice3D.BACKGROUND_SOUND) {
+ thread.setSampleRate(((JSPositionalSample)sample).getSecondIndex(),
+ scaleFactor);
+ thread.setSampleRate(((JSPositionalSample)sample).getReverbIndex(),
+ scaleFactor);
+ }
+ **********/
+ }
+ else {
+ if (internalErrors)
+ debugPrintln(
+ "JavaSoundMixer: Internal Error scaleSampleRate dataType " +
+ dataType + " invalid");
+ }
+ }
+
+ /*
+ * Methods called during rendering
+ */
+ void calcReverb(JSSample sample) {
+ /*
+ * Java Sound reverb parameters are a subset of Java 3D parameters
+ */
+ int dataType = sample.getDataType();
+ int soundType = sample.getSoundType();
+ float decay = auralParams.decayTime;
+ float delay = auralParams.reverbDelay * auralParams.rolloff;
+ float reflection = auralParams.reflectionCoefficient;
+ int order = auralParams.reverbOrder;
+ /*
+ * Remember Coeff change is choosen over Order change if BOTH made
+ * otherwise the last one changed take precidence.
+ */
+ if (auralParams.reflectionCoefficient == 0.0f ||
+ auralParams.reverbCoefficient == 0.0f)
+ auralParams.reverbFlag = false;
+ else {
+ auralParams.reverbFlag = true;
+ if (order > 0) {
+ // clamp reverb decay time to order*delay
+ float clampedTime = order * delay;
+ if ( clampedTime < decay)
+ decay = clampedTime;
+ }
+ if (delay < 100.0f) {
+ // "small" reverberant space
+ if (decay <= 1500.0f)
+ auralParams.reverbType = 2;
+ else
+ auralParams.reverbType = 4;
+ }
+ else if (delay < 500.0f) {
+ // "medium" reverberant space
+ if (decay <= 1500.0f)
+ auralParams.reverbType = 3;
+ else
+ auralParams.reverbType = 6;
+ }
+ else { // delay >= 500.0f
+ // "large" reverberant space
+ if (decay <= 1500.0f)
+ auralParams.reverbType = 6;
+ else
+ auralParams.reverbType = 5;
+ }
+ }
+
+ if (debugFlag)
+ debugPrintln("JavaSoundMixer: setReverb for " +
+ sample + ", type = " + auralParams.reverbType + ", flag = " + auralParams.reverbFlag);
+
+ auralParams.reverbDirty = 0; // clear the attribute reverb dirty flags
+ }
+
+ /*
+ * Interal method for setting reverb parameters called during rendering.
+ * This not called by SoundScheduler.
+ */
+ void setReverb(JSSample sample) {
+ /*
+ * Only third sample of multisample sounds has reverb parameters set.
+ * For now, only positional and directional sounds are reverberated.
+ */
+ int soundType = sample.getSoundType();
+ int dataType = sample.getDataType();
+
+ // QUESTION: Should reverb be applied to background sounds?
+ if ( (soundType == AudioDevice3D.CONE_SOUND) ||
+ (soundType == AudioDevice3D.POINT_SOUND) ) {
+ if (debugFlag)
+ debugPrintln("setReverb called with type, on = " +
+ auralParams.reverbType + ", " + auralParams.reverbFlag);
+ if (sample == null)
+ return;
+ JSPositionalSample posSample = (JSPositionalSample)sample;
+ if (posSample.channel == null)
+ return;
+
+ /**********
+ // NOTE: no support for reverb channel yet...
+ int reverbIndex = posSample.getReverbIndex();
+ **********/
+ if (dataType == JSSample.STREAMING_AUDIO_DATA) {
+ JSStream stream = (JSStream)posSample.channel;
+ stream.setSampleReverb(auralParams.reverbType, auralParams.reverbFlag);
+ }
+ else if (dataType == JSSample.BUFFERED_AUDIO_DATA) {
+ JSClip clip = (JSClip)posSample.channel;
+ clip.setSampleReverb(auralParams.reverbType, auralParams.reverbFlag);
+ }
+ /**********
+ // TODO:
+ else if (dataType == JSSample.STREAMING_MIDI_DATA ||
+ dataType == JSSample.BUFFERED_MIDI_DATA) {
+ JSMidi.setSampleReverb(reverbIndex,
+ auralParams.reverbType, auralParams.reverbFlag);
+ }
+ **********/
+ else {
+ if (internalErrors)
+ debugPrintln( "JavaSoundMixer: Internal Error setReverb " +
+ "dataType " + dataType + " invalid");
+ }
+ }
+ }
+
+ // TEMPORARY: Override of method due to bug in Java Sound
+ public void setLoop(int index, int count) {
+ JSSample sample = null;
+ if ((sample = (JSSample)getSample(index)) == null)
+ return;
+ int dataType = sample.getDataType();
+
+ // WORKAROUND:
+ // Bug in Java Sound engine hangs when INFINITE_LOOP count
+ // for Audio Wave data. Leave count unchanged for Midi data.
+ if (dataType==JSSample.STREAMING_AUDIO_DATA ||
+ dataType==JSSample.BUFFERED_AUDIO_DATA) {
+ if (count == Sound.INFINITE_LOOPS) {
+ // LoopCount of 'loop Infinitely' forced to largest positive int
+ count = 0x7FFFFFF;
+ }
+ }
+ super.setLoop(index, count);
+ return;
+ }
+
+ // Perform device specific filtering
+ // Assumes that this is called for positional and directional sounds
+ // not background sounds, so there are at lease two samples assigned
+ // per sound.
+ // TODO: remove assumption from method
+ void setFilter(int index, boolean filterFlag, float filterFreq) {
+ JSPositionalSample posSample = null;
+ if ((posSample = (JSPositionalSample)getSample(index)) == null)
+ return;
+ if (posSample.channel == null)
+ return;
+ int dataType = posSample.getDataType();
+
+ // Filtering can NOT be performed on MIDI Songs
+ if (dataType == JSSample.STREAMING_MIDI_DATA ||
+ dataType == JSSample.BUFFERED_MIDI_DATA) {
+ return;
+ }
+
+ /****
+ // TODO: multiple clips per channel
+ int secondIndex = posSample.getSecondIndex();
+ *****/
+ if (dataType == JSSample.BUFFERED_AUDIO_DATA) {
+ JSClip clip = (JSClip)posSample.channel;
+ clip.setSampleFiltering(filterFlag,filterFreq);
+ /*****
+ JSClip.setSampleFiltering(econdIndex, filterFlag, filterFreq);
+ ******/
+ }
+ else { // dataType == JSSample.STREAMING_AUDIO_DATA
+ JSStream stream = (JSStream)posSample.channel;
+ stream.setSampleFiltering(filterFlag,filterFreq);
+ /*****
+ JSStream.setSampleFiltering(secondIndex, ilterFlag, filterFreq);
+ ******/
+ }
+ // QUESTION: should reverb channel be filtered???
+
+ if (debugFlag) {
+ debugPrintln("JavaSoundMixer:setFilter " +
+ "of non-backgroundSound by (" +
+ filterFlag + ", " + filterFreq + ")");
+ }
+ }
+ //
+ // Set overall gain for device
+ // @since Java 3D 1.3
+ //
+ public void setGain(float scaleFactor) {
+ float oldDeviceGain = deviceGain;
+ float gainFactor = scaleFactor/oldDeviceGain;
+ // TODO: for each sample, change gain by gainFactor
+ deviceGain = scaleFactor; // set given scalefactor as new device gain
+ return;
+ }
+
+ /*
+ * Set sample specific sample rate scale factor gain
+ * @since Java 3D 1.3
+ */
+ public void setRateScaleFactor(int index, float rateScaleFactor) {
+ JSSample sample = null;
+ if ((sample = (JSSample)getSample(index)) == null)
+ return;
+ sample.setRateScaleFactor(rateScaleFactor);
+ this.scaleSampleRate(index, rateScaleFactor);
+ }
+
+ /**
+ * Pauses audio device engine without closing the device and associated
+ * threads.
+ * Causes all cached sounds to be paused and all streaming sounds to be
+ * stopped.
+ */
+ public void pause() {
+ pause = PAUSE_PENDING;
+ // TODO: pause all sounds
+ return;
+ }
+ /**
+ * Resumes audio device engine (if previously paused) without reinitializing * the device.
+ * Causes all paused cached sounds to be resumed and all streaming sounds
+ * restarted.
+ */
+ public void resume() {
+ pause = RESUME_PENDING;
+ // TODO: unpause all sounds
+ return;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/internal/BufferWrapper.java b/src/classes/share/com/sun/j3d/internal/BufferWrapper.java
new file mode 100755
index 0000000..40370bc
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/internal/BufferWrapper.java
@@ -0,0 +1,186 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.internal;
+
+import javax.media.j3d.J3DBuffer;
+import java.nio.Buffer;
+
+/**
+ * NIO Buffers are new in Java 1.4 but we need to run on 1.3
+ * as well, so this class was created to hide the NIO classes
+ * from non-1.4 Java 3D users.
+ *
+ * <p>
+ * NOTE: We no longer need to support JDK 1.3 as of the Java 3D 1.3.2
+ * community source release on java.net. We should be able to get rid
+ * of this class.
+ */
+
+public abstract class BufferWrapper {
+
+ /**
+ * Value returned from getBufferType(), this indicates
+ * that the BufferWrapper contains a null buffer.
+ */
+ public static final int TYPE_NULL = 0;
+
+ /**
+ * Value returned from getBufferType(), this indicates
+ * that the BufferWrapper does not hold data of type
+ * byte, float, or double.
+ */
+ public static final int TYPE_UNKNOWN = 1;
+
+ /**
+ * Value returned from getBufferType(), this indicates
+ * that the BufferWrapper contains a java.nio.ByteBuffer.
+ */
+ public static final int TYPE_BYTE = 2;
+
+ /**
+ * Value returned from getBufferType(), this indicates
+ * that the BufferWrapper contains a java.nio.FloatBuffer.
+ */
+ public static final int TYPE_FLOAT = 3;
+
+ /**
+ * Value returned from getBufferType(), this indicates
+ * that the BufferWrapper contains a java.nio.DoubleBuffer.
+ */
+ public static final int TYPE_DOUBLE = 4;
+
+ /**
+ * Never used - this class is abstract.
+ */
+ public BufferWrapper() {
+ }
+
+ /**
+ * Must be implemented by sublasses.
+ */
+ abstract Buffer getBuffer();
+
+ /**
+ * @return Buffer as object of type Object.
+ */
+ public Object getBufferAsObject() {
+ return getBuffer();
+ }
+
+ // Wrapper for all relevant Buffer methods.
+
+ /**
+ * @return This buffer's capacity (set at initialization in
+ * allocateDirect() ).
+ * @see ByteBufferWrapper#allocateDirect
+ */
+ public int capacity() {
+ return getBuffer().capacity();
+ }
+
+ /**
+ * @return This buffer's limit.
+ */
+ public int limit() {
+ return getBuffer().limit();
+ }
+
+ /**
+ * @return This buffer's position.
+ */
+ public int position() {
+ return getBuffer().position();
+ }
+
+ /**
+ * Sets this buffer's position.
+ * @return This buffer.
+ */
+ public BufferWrapper position(int newPosition){
+ getBuffer().position(newPosition);
+ return this;
+ }
+
+ /**
+ * Resets this buffer's position to the previously marked
+ * position.
+ * @return This buffer.
+ */
+ public BufferWrapper rewind() {
+ getBuffer().rewind();
+ return this;
+ }
+
+ /**
+ * @return An integer indicating the type of data held in
+ * this buffer.
+ * @see #TYPE_NULL
+ * @see #TYPE_BYTE
+ * @see #TYPE_FLOAT
+ * @see #TYPE_DOUBLE
+ * @see #TYPE_UNKNOWN
+ */
+ public static int getBufferType(J3DBuffer b) {
+ int bufferType;
+ Buffer buffer = b.getBuffer();
+
+ if (buffer == null) {
+ bufferType = TYPE_NULL;
+ }
+ else if (buffer instanceof java.nio.ByteBuffer) {
+ bufferType = TYPE_BYTE;
+ }
+ else if (buffer instanceof java.nio.FloatBuffer) {
+ bufferType = TYPE_FLOAT;
+ }
+ else if (buffer instanceof java.nio.DoubleBuffer) {
+ bufferType = TYPE_DOUBLE;
+ }
+ else {
+ bufferType = TYPE_UNKNOWN;
+ }
+ return bufferType;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/internal/ByteBufferWrapper.java b/src/classes/share/com/sun/j3d/internal/ByteBufferWrapper.java
new file mode 100755
index 0000000..61f2c58
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/internal/ByteBufferWrapper.java
@@ -0,0 +1,197 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.internal;
+
+import javax.media.j3d.J3DBuffer;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * NIO Buffers are new in Java 1.4 but we need to run on 1.3
+ * as well, so this class was created to hide the NIO classes
+ * from non-1.4 Java 3D users.
+ *
+ * <p>
+ * NOTE: We no longer need to support JDK 1.3 as of the Java 3D 1.3.2
+ * community source release on java.net. We should be able to get rid
+ * of this class.
+ */
+
+public class ByteBufferWrapper extends BufferWrapper {
+
+ private ByteBuffer buffer = null;
+
+ /**
+ * Constructor initializes buffer with a
+ * java.nio.ByteBuffer object.
+ */
+ public ByteBufferWrapper(ByteBuffer buffer) {
+ this.buffer = buffer;
+ }
+
+ /**
+ * Constructor initializes buffer with a
+ * javax.media.j3d.J3DBuffer object.
+ */
+ public ByteBufferWrapper(J3DBuffer b) {
+ buffer = (ByteBuffer)(b.getBuffer());
+ }
+
+ /**
+ * Allocate a direct ByteBuffer with the given capacity.
+ * @return New ByteBufferWrapper containing the
+ * new buffer.
+ */
+ public static ByteBufferWrapper allocateDirect(int capacity) {
+ ByteBuffer bb = ByteBuffer.allocateDirect(capacity);
+ return new ByteBufferWrapper(bb);
+ }
+
+ /**
+ * Returns the java.nio.Buffer contained within this
+ * ByteBufferWrapper.
+ */
+ public java.nio.Buffer getBuffer() {
+ return this.buffer;
+ }
+
+ // Wrapper for all relevant ByteBuffer methods.
+
+ /**
+ * @return A boolean indicating whether the java.nio.Buffer
+ * object contained within this ByteBuffer is direct or
+ * indirect.
+ */
+ public boolean isDirect() {
+ return buffer.isDirect();
+ }
+
+ /**
+ * Reads the byte at this buffer's current position,
+ * and then increments the position.
+ */
+ public byte get() {
+ return buffer.get();
+ }
+
+ /**
+ * Reads the byte at the given offset into the buffer.
+ */
+ public byte get(int index) {
+ return buffer.get(index);
+ }
+
+ /**
+ * Bulk <i>get</i> method. Transfers <code>dst.length</code>
+ * bytes from
+ * the buffer to the destination array and increments the
+ * buffer's position by <code>dst.length</code>.
+ */
+ public ByteBufferWrapper get(byte[] dst) {
+ buffer.get(dst);
+ return this;
+ }
+
+ /**
+ * Bulk <i>get</i> method. Transfers <i>length</i> bytes
+ * from the buffer starting at position <i>offset</i> into
+ * the destination array.
+ */
+ public ByteBufferWrapper get(byte[] dst, int offset, int length) {
+ buffer.get(dst, offset, length);
+ return this;
+ }
+
+ /**
+ * Returns the byte order of this buffer.
+ */
+ public ByteOrderWrapper order() {
+ if ( buffer.order()==ByteOrder.BIG_ENDIAN ) return ByteOrderWrapper.BIG_ENDIAN;
+ else return ByteOrderWrapper.LITTLE_ENDIAN;
+ }
+
+ /**
+ * Modifies this buffer's byte order.
+ */
+ public ByteBufferWrapper order(ByteOrderWrapper bo)
+ {
+ if ( bo == ByteOrderWrapper.BIG_ENDIAN ) buffer.order( ByteOrder.BIG_ENDIAN );
+ else buffer.order( ByteOrder.LITTLE_ENDIAN );
+ return this;
+ }
+
+ /**
+ * Creates a view of this ByteBufferWrapper as a
+ * FloatBufferWrapper. Uses the correct
+ */
+ public FloatBufferWrapper asFloatBuffer() {
+ return new FloatBufferWrapper( buffer.asFloatBuffer() );
+ }
+
+ /**
+ * Creates a view of this ByteBufferWrapper as a
+ * DoubleBufferWrapper.
+ */
+ public DoubleBufferWrapper asDoubleBuffer() {
+ return new DoubleBufferWrapper( buffer.asDoubleBuffer() );
+ }
+
+ /**
+ * Bulk <i>put</i> method. Transfers <code>src.length</code>
+ * bytes into the buffer at the current position.
+ */
+ public ByteBufferWrapper put(byte[] src) {
+ buffer.put(src);
+ return this;
+ }
+
+ /**
+ * Creates and returns a J3DBuffer object containing the
+ * buffer in this ByteBufferWrapper object.
+ */
+ public J3DBuffer getJ3DBuffer() {
+ return new J3DBuffer( buffer );
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/internal/ByteOrderWrapper.java b/src/classes/share/com/sun/j3d/internal/ByteOrderWrapper.java
new file mode 100755
index 0000000..bfaa373
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/internal/ByteOrderWrapper.java
@@ -0,0 +1,101 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.internal;
+
+import javax.media.j3d.J3DBuffer;
+import java.nio.Buffer;
+import java.nio.ByteOrder;
+
+/**
+ * NIO Buffers are new in Java 1.4 but we need to run on 1.3
+ * as well, so this class was created to hide the NIO classes
+ * from non-1.4 Java 3D users.
+ *
+ * <p>
+ * Typesafe enum for byte orders.
+ *
+ * <p>
+ * NOTE: We no longer need to support JDK 1.3 as of the Java 3D 1.3.2
+ * community source release on java.net. We should be able to get rid
+ * of this class.
+ */
+
+public final class ByteOrderWrapper {
+
+ private final String enum_name;
+
+ /**
+ * Private constructor is only called from static initializers
+ * in this class.
+ */
+ private ByteOrderWrapper(String name) {
+ enum_name = name;
+ }
+
+ /**
+ * Static initializer creates object of this type.
+ */
+ public static final ByteOrderWrapper BIG_ENDIAN =
+ new ByteOrderWrapper("BIG_ENDIAN");
+
+ /**
+ * Static initializer creates object of this type.
+ */
+ public static final ByteOrderWrapper LITTLE_ENDIAN =
+ new ByteOrderWrapper("LITTLE_ENDIAN");
+
+ public String toString() {
+ return enum_name;
+ }
+
+ /**
+ * Returns the native byte order of the host system.
+ */
+ public static ByteOrderWrapper nativeOrder() {
+ if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) {
+ return ByteOrderWrapper.BIG_ENDIAN;
+ } else return ByteOrderWrapper.LITTLE_ENDIAN;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/internal/Distance.java b/src/classes/share/com/sun/j3d/internal/Distance.java
new file mode 100644
index 0000000..f49659f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/internal/Distance.java
@@ -0,0 +1,1096 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// --------------------------------------------------
+//
+// Distance routines, ported from:
+//
+// Magic Software, Inc.
+// http://www.magic-software.com
+// http://www.wild-magic.com
+// Copyright (c) 2004. All Rights Reserved
+//
+// The Wild Magic Library (WML) source code is supplied under the terms of
+// the license agreement http://www.magic-software.com/License/WildMagic.pdf
+// and may not be copied or disclosed except in accordance with the terms of
+// that agreement.
+//
+// --------------------------------------------------
+
+package com.sun.j3d.internal;
+
+import javax.vecmath.*;
+
+/**
+ * Utility class used to calculate distance. Contains static methods
+ * used by picking method to determine intersections.
+ */
+
+public class Distance {
+ /* Threshold factor to determine if two lines are parallel */
+ static final double FUZZ = 1E-5;
+
+ /* Utility method, for easy switch between distance and squared distance */
+ private static final double DIST (double in) {
+ // return Math.sqrt (Math.abs (in));
+ return Math.abs (in);
+ }
+
+ /**
+ * Minimum ray to segment distance.
+ *
+ * @param rayorig Origin of the ray
+ * @param raydir Direction of the ray
+ * @param segstart Segment start point
+ * @param segend Segment end point
+ * @return the square of the minimum distance from the ray to the segment
+ */
+ static public double rayToSegment (Point3d rayorig,
+ Vector3d raydir,
+ Point3d segstart,
+ Point3d segend) {
+ return rayToSegment (rayorig, raydir, segstart, segend, null, null, null);
+ }
+
+ /**
+ * Minimum ray to segment distance. Returns the square of the distance.
+ *
+ * @param rayorig Origin of the ray
+ *
+ * @param raydir Direction of the ray
+ *
+ * @param segstart Segment start point
+ *
+ * @param segend Segment end point
+ *
+ * @param rayint If non-null, will be filled with the coordinates of
+ * the point corresponding to the minimum distance on the ray.
+ *
+ * @param segint If non-null, will be filled with the coordinates of
+ * the point corresponding to the minimum distance on the segment.
+ *
+ * @param param An array of two doubles, will be filled with the
+ * parametric factors used to find the point of shortest distance on
+ * each primitive (ray = O +sD, with O=origin and
+ * D=direction). param[0] will contain the parameter for the ray,
+ * and param[1] the parameter for the segment.
+ *
+ * @return the square of the minimum distance from the ray to the
+ * segment
+ */
+ static public double rayToSegment (Point3d rayorig,
+ Vector3d raydir,
+ Point3d segstart,
+ Point3d segend,
+ Point3d rayint,
+ Point3d segint,
+ double[] param) {
+ double s, t;
+
+ Vector3d diff = new Vector3d();
+ diff.sub (rayorig,segstart);
+ Vector3d segdir = new Vector3d();
+ segdir.sub (segend, segstart);
+ /*
+ System.out.println (rayorig + "\n" + raydir + "\n" + segstart + "\n" +
+ segdir);
+ */
+ double A = raydir.dot (raydir);//Dot(ray.m,ray.m);
+ double B = -raydir.dot (segdir);//-Dot(ray.m,seg.m);
+ double C = segdir.dot (segdir);//Dot(seg.m,seg.m);
+ double D = raydir.dot (diff);//Dot(ray.m,diff);
+ double E; // -Dot(seg.m,diff), defer until needed
+ double F = diff.dot (diff);//Dot(diff,diff);
+ double det = Math.abs(A*C-B*B); // A*C-B*B = |Cross(M0,M1)|^2 >= 0
+
+ double tmp;
+
+ if (det >= FUZZ) {
+ // ray and segment are not parallel
+ E = -segdir.dot (diff);//-Dot(seg.m,diff);
+ s = B*E-C*D;
+ t = B*D-A*E;
+
+ if (s >= 0) {
+ if (t >= 0) {
+ if (t <= det) { // region 0
+ // minimum at interior points of ray and segment
+ double invDet = 1.0f/det;
+ s *= invDet;
+ t *= invDet;
+ if (rayint!=null) rayint.scaleAdd (s, raydir, rayorig);
+ if (segint!=null) segint.scaleAdd (t, segdir, segstart);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(s*(A*s+B*t+2*D)+t*(B*s+C*t+2*E)+F);
+ }
+ else { // region 1
+
+ t = 1;
+ if (D >= 0) {
+ s = 0;
+ if (rayint!=null) rayint.set (rayorig);
+ if (segint!=null) segint.set (segend);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(C+2*E+F);
+ }
+ else {
+ s = -D/A;
+ if (rayint!=null) rayint.scaleAdd (s, raydir, rayorig);
+ if (segint!=null) segint.set (segend);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST((D+2*B)*s+C+2*E+F);
+ }
+ }
+ }
+ else { // region 5
+ t = 0;
+ if (D >= 0) {
+ s = 0;
+ if (rayint != null) rayint.set (rayorig);
+ if (segint != null) segint.set (segstart);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(F);
+ }
+ else {
+ s = -D/A;
+ if (rayint != null) rayint.scaleAdd (s, raydir, rayorig);
+ if (segint != null) segint.set (segstart);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(D*s+F);
+ }
+ }
+ }
+ else {
+ if (t <= 0) { // region 4
+ if (D < 0) {
+ s = -D/A;
+ t = 0;
+ if (rayint != null) rayint.scaleAdd (s, raydir, rayorig);
+ if (segint != null) segint.set (segstart);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(D*s+F);
+ }
+ else {
+ s = 0;
+ if (E >= 0) {
+ t = 0;
+ if (rayint != null) rayint.set (rayorig);
+ if (segint != null) segint.set (segstart);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(F);
+ }
+ else if (-E >= C) {
+ t = 1;
+ if (rayint != null) rayint.set (rayorig);
+ if (segint != null) segint.set (segend);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(C+2*E+F);
+ }
+ else {
+ t = -E/C;
+ if (rayint != null) rayint.set (rayorig);
+ if (segint != null) segint.scaleAdd (t, segdir, segstart);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(E*t+F);
+ }
+ }
+ }
+ else if (t <= det) { // region 3
+ s = 0;
+ if (E >= 0) {
+ t = 0;
+ if (rayint != null) rayint.set (rayorig);
+ if (segint != null) segint.set (segstart);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(F);
+ }
+ else if (-E >= C) {
+ t = 1;
+ if (rayint != null) rayint.set (rayorig);
+ if (segint != null) segint.set (segend);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(C+2*E+F);
+ }
+ else {
+ t = -E/C;
+ if (rayint != null) rayint.set (rayorig);
+ if (segint != null) segint.scaleAdd (t, segdir, segstart);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(E*t+F);
+ }
+ }
+ else { // region 2
+ tmp = B+D;
+ if (tmp < 0) {
+ s = -tmp/A;
+ t = 1;
+ if (rayint != null) rayint.scaleAdd (s, raydir, rayorig);
+ if (segint != null) segint.set (segend);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(tmp*s+C+2*E+F);
+ }
+ else {
+ s = 0;
+ if (E >= 0) {
+ t = 0;
+ if (rayint != null) rayint.set (rayorig);
+ if (segint != null) segint.set (segstart);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(F);
+ }
+ else if (-E >= C) {
+ t = 1;
+ if (rayint != null) rayint.set (rayorig);
+ if (segint != null) segint.set (segend);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(C+2*E+F);
+ }
+ else {
+ t = -E/C;
+ if (rayint != null) rayint.set (rayorig);
+ if (segint != null) segint.scaleAdd (t, segdir, segstart);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(E*t+F);
+ }
+ }
+ }
+ }
+ }
+ else {
+ // ray and segment are parallel
+ if (B > 0) {
+ // opposite direction vectors
+ t = 0;
+ if (D >= 0) {
+ s = 0;
+ if (rayint != null) rayint.set (rayorig);
+ if (segint != null) segint.set (segstart);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(F);
+ }
+ else {
+ s = -D/A;
+ if (rayint != null) rayint.scaleAdd (s, raydir, rayorig);
+ if (segint != null) segint.set (segstart);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(D*s+F);
+ }
+ }
+ else {
+ // same direction vectors
+ E = segdir.dot (diff);//-Dot(seg.m,diff);
+ t = 1;
+ tmp = B+D;
+ if (tmp >= 0) {
+ s = 0;
+ if (rayint != null) rayint.set (rayorig);
+ if (segint != null) segint.set (segend);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(C+2*E+F);
+ }
+ else {
+ s = -tmp/A;
+ if (rayint != null) rayint.scaleAdd (s, raydir, rayorig);
+ if (segint != null) segint.set (segend);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(tmp*s+C+2*E+F);
+ }
+ }
+ }
+ }
+
+ /**
+ * Minimum ray to ray distance. Returns the square of the distance.
+ *
+ * @param ray0orig Origin of ray 0
+ * @param ray0dir Direction of ray 0
+ * @param ray1orig Origin of ray 1
+ * @param ray1dir Direction of ray 1
+ * @return the square of the minimum distance from the ray to the segment
+ */
+ static public double rayToRay (Point3d ray0orig,
+ Vector3d ray0dir,
+ Point3d ray1orig,
+ Vector3d ray1dir) {
+ return rayToRay (ray0orig, ray0dir, ray1orig, ray1dir, null, null, null);
+ }
+
+ /**
+ * Minimum ray to ray distance. Returns the square of the distance.
+ *
+ * @param ray0orig Origin of ray 0
+ *
+ * @param ray0dir Direction of ray 0
+ *
+ * @param ray1orig Origin of ray 1
+ *
+ * @param ray1dir Direction of ray 1
+ *
+ * @param ray0int If non-null, will be filled with the coordinates
+ * of the point corresponding to the minimum distance on ray 0.
+ *
+ * @param ray1int If non-null, will be filled with the coordinates
+ * of the point corresponding to the minimum distance on ray 1.
+ *
+ * @param param An array of two doubles, will be filled with the
+ * parametric factors used to find the point of shortest distance on
+ * each primitive (ray = O +sD, with O=origin and
+ * D=direction). param[0] will contain the parameter for ray0, and
+ * param[1] the parameter for ray1.
+ *
+ * @return the square of the minimum distance from the ray to the segment
+ */
+ static public double rayToRay (Point3d ray0orig,
+ Vector3d ray0dir,
+ Point3d ray1orig,
+ Vector3d ray1dir,
+ Point3d ray0int,
+ Point3d ray1int,
+ double[] param) {
+
+ double s, t;
+
+ Vector3d diff = new Vector3d();
+ diff.sub (ray0orig, ray1orig);
+
+ double A = ray0dir.dot (ray0dir); //Dot(ray0.m,ray0.m);
+ double B = -ray0dir.dot (ray1dir); //-Dot(ray0.m,ray1.m);
+ double C = ray1dir.dot (ray1dir); //Dot(ray1.m,ray1.m);
+ double D = ray0dir.dot (diff); //Dot(ray0.m,diff);
+ double E; // -Dot(ray1.m,diff), defer until needed
+ double F = diff.dot (diff); //Dot(diff,diff);
+ double det = Math.abs(A*C-B*B); // A*C-B*B = |Cross(M0,M1)|^2 >= 0
+ /*
+ System.out.println (ray0orig + "\n" + ray0dir + "\n" +
+ ray1orig + "\n" + ray1dir);
+ System.out.println (A + " " + B + " " + C + " " + D + " " + F + " " + det);
+ */
+ if (det >= FUZZ) {
+ // rays are not parallel
+ E = -ray1dir.dot (diff); //-Dot(ray1.m,diff);
+ s = B*E-C*D;
+ t = B*D-A*E;
+
+ if (s >= 0) {
+ if (t >= 0) { // region 0 (interior)
+ // minimum at two interior points of rays
+ double invDet = 1.0f/det;
+ s *= invDet;
+ t *= invDet;
+ if (ray0int != null) ray0int.scaleAdd (s, ray0dir, ray0orig);
+ if (ray1int != null) ray1int.scaleAdd (t, ray1dir, ray1orig);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(s*(A*s+B*t+2*D)+t*(B*s+C*t+2*E)+F);
+ }
+ else { // region 3 (side)
+ t = 0;
+ if (D >= 0) {
+ s = 0;
+ if (ray0int != null) ray0int.set (ray0orig);
+ if (ray1int != null) ray1int.set (ray1orig);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(F);
+ }
+ else {
+ s = -D/A;
+ if (ray0int != null) ray0int.scaleAdd (s, ray0dir, ray0orig);
+ if (ray1int != null) ray1int.set (ray1orig);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(D*s+F);
+ }
+ }
+ }
+ else {
+ if (t >= 0) { // region 1 (side)
+ s = 0;
+ if (E >= 0) {
+ t = 0;
+ if (ray0int != null) ray0int.set (ray0orig);
+ if (ray1int != null) ray1int.set (ray1orig);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(F);
+ }
+ else {
+ t = -E/C;
+ if (ray0int != null) ray0int.set (ray0orig);
+ if (ray1int != null) ray1int.scaleAdd (t, ray1dir, ray1orig);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(E*t+F);
+ }
+ }
+ else { // region 2 (corner)
+ if (D < 0) {
+ s = -D/A;
+ t = 0;
+ if (ray0int != null) ray0int.scaleAdd (s, ray0dir, ray0orig);
+ if (ray1int != null) ray1int.set (ray1orig);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(D*s+F);
+ }
+ else {
+ s = 0;
+ if (E >= 0) {
+ t = 0;
+ if (ray0int != null) ray0int.set (ray0orig);
+ if (ray1int != null) ray1int.set (ray1orig);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(F);
+ }
+ else {
+ t = -E/C;
+ if (ray0int != null) ray0int.set (ray0orig);
+ if (ray1int != null) ray1int.scaleAdd (t, ray1dir, ray1orig);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(E*t+F);
+ }
+ }
+ }
+ }
+ }
+ else {
+ // rays are parallel
+ if (B > 0) {
+ // opposite direction vectors
+ t = 0;
+ if (D >= 0) {
+ s = 0;
+ if (ray0int != null) ray0int.set (ray0orig);
+ if (ray1int != null) ray1int.set (ray1orig);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(F);
+ }
+ else {
+ s = -D/A;
+ if (ray0int != null) ray0int.scaleAdd (s, ray0dir, ray0orig);
+ if (ray1int != null) ray1int.set (ray1orig);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(D*s+F);
+ }
+ }
+ else {
+ // same direction vectors
+ if (D >= 0) {
+ E = ray1dir.dot (diff); //-Dot(ray1.m,diff);
+ s = 0;
+ t = -E/C;
+ if (ray0int != null) ray0int.set (ray0orig);
+ if (ray1int != null) ray1int.scaleAdd (t, ray1dir, ray1orig);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(E*t+F);
+ }
+ else {
+ s = -D/A;
+ t = 0;
+ if (ray0int != null) ray0int.scaleAdd (s, ray0dir, ray0orig);
+ if (ray1int != null) ray1int.set (ray1orig);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(D*s+F);
+ }
+ }
+ }
+ }
+
+ /**
+ * Minimum pt to ray distance. Returns the square of the distance.
+ * @param pt The point
+ * @param rayorig Origin of the ray
+ * @param raydir Direction of the ray
+ * @return the square of the minimum distance between the point and the ray
+ */
+ static public double pointToRay (Point3d pt,
+ Point3d rayorig,
+ Vector3d raydir) {
+ return pointToRay (pt, rayorig, raydir, null, null);
+ }
+
+ /**
+ * Minimum pt to ray distance. Returns the square of the distance.
+ *
+ * @param pt The point
+ *
+ * @param rayorig Origin of the ray
+ *
+ * @param raydir Direction of the ray
+ *
+ * @param rayint If non-null, will be filled with the coordinates of
+ * the point corresponding to the minimum distance on the ray.
+ *
+ * @param param An array of one double, will be filled with the
+ * parametric factors used to find the point of shortest distance on
+ * the ray (ray = O +sD, with O=origin and D=direction). param[0]
+ * will contain the parameter for the ray.
+ *
+ * @return the square of the minimum distance between the point and the ray
+ */
+ static public double pointToRay (Point3d pt,
+ Point3d rayorig,
+ Vector3d raydir,
+ Point3d rayint,
+ double[] param) {
+
+ double t;
+
+ Vector3d diff = new Vector3d();
+ diff.sub (pt, rayorig);
+ t = raydir.dot (diff); //Dot(ray.m,diff);
+
+ if (t <= 0.0) {
+ t = 0.0; // behind start of ray
+ if (rayint != null) rayint.set (rayorig);
+ if (param != null) { param[0] = t; }
+ } else {
+ t /= raydir.dot (raydir); //Dot(ray.m,ray.m);
+ diff.scaleAdd (-t, raydir, diff); // diff = diff - t*ray.m;
+ if (rayint != null) rayint.scaleAdd (t, raydir, rayorig);
+ if (param != null) { param[0] = t; }
+ }
+ return diff.dot(diff);
+ }
+
+ /**
+ * Minimum pt to segment distance. Returns the square of the distance.
+ */
+ static public double pointToSegment (Point3d pt,
+ Point3d segstart,
+ Point3d segend) {
+ return pointToSegment (pt, segstart, segend, null, null);
+ }
+
+ /**
+ * Minimum pt to segment distance. Returns the square of the distance.
+ */
+ static public double pointToSegment (Point3d pt,
+ Point3d segstart,
+ Point3d segend,
+ Point3d segint,
+ double[] param) {
+
+ double t;
+ Vector3d segdir = new Vector3d ();
+ segdir.sub (segend, segstart);
+ Vector3d diff = new Vector3d();
+ diff.sub (pt,segstart);
+ t = segdir.dot (diff); //Dot(seg.m,diff);
+
+ if (t <= 0.0) {
+ t = 0.0f;
+ if (segint != null) segint.set (segstart);
+ if (param != null) { param[0] = t; }
+ }
+ else {
+ double mDotm = segdir.dot (segdir); //Dot(seg.m,seg.m);
+ if (t >= mDotm) {
+ t = 1.0f;
+ diff.sub (segdir);
+ if (segint != null) segint.set (segend);
+ if (param != null) { param[0] = t; }
+ }
+ else {
+ t /= mDotm;
+ diff.scaleAdd (-t, segdir, diff); //diff = diff - t*seg.m;
+ if (segint != null) segint.scaleAdd (t, segdir, segstart);
+ if (param != null) { param[0] = t; }
+ }
+ }
+ return diff.dot(diff); //DIST(diff);
+ }
+
+
+ /**
+ * Minimum segment to segment distance. Returns the square of the distance.
+ * @param seg0start the start of segment 0
+ * @param seg0end the end of segment 0
+ * @param seg1start the start of segment 1
+ * @param seg1end the end of segment 1
+ * @return the square of the minimum distance from segment to segment
+ */
+ static public double segmentToSegment (Point3d seg0start,
+ Point3d seg0end,
+ Point3d seg1start,
+ Point3d seg1end) {
+ return segmentToSegment (seg0start, seg0end, seg1start, seg1end,
+ null, null, null);
+ }
+
+ /**
+ * Minimum segment to segment distance. Returns the square of the distance.
+ *
+ * @param seg0start the start of segment 0
+ *
+ * @param seg0end the end of segment 0
+ *
+ * @param seg1start the start of segment 1
+ *
+ * @param seg1end the end of segment 1
+ *
+ * @param seg0int If non-null, will be filled with the coordinates
+ * of the point corresponding to the minimum distance on segment 0.
+ *
+ * @param seg1int If non-null, will be filled with the coordinates
+ * of the point corresponding to the minimum distance on segment 1.
+ *
+ * @param param An array of two doubles, will be filled with the
+ * parametric factors used to find the point of shortest distance on
+ * each primitive (segment = O +sD, with O=origin and
+ * D=direction). param[0] will contain the parameter for segment 0,
+ * and param[1] the parameter for segment 1.
+ *
+ * @return the square of the minimum distance from segment to segment
+ */
+ static public double segmentToSegment (Point3d seg0start,
+ Point3d seg0end,
+ Point3d seg1start,
+ Point3d seg1end,
+ Point3d seg0int,
+ Point3d seg1int,
+ double[] param) {
+ double s,t;
+
+ Vector3d diff = new Vector3d();
+ diff.sub (seg0start,seg1start);
+
+ Vector3d seg0dir = new Vector3d();
+ seg0dir.sub (seg0end, seg0start);
+ Vector3d seg1dir = new Vector3d();
+ seg1dir.sub (seg1end, seg1start);
+
+ double A = seg0dir.dot (seg0dir); //Dot(seg0dir,seg0dir);
+ double B = -seg0dir.dot (seg1dir); //-Dot(seg0dir,seg1dir);
+ double C = seg1dir.dot (seg1dir); //Dot(seg1dir,seg1dir);
+ double D = seg0dir.dot (diff); //Dot(seg0dir,diff);
+ double E; // -Dot(seg1dir,diff), defer until needed
+ double F = diff.dot (diff); //Dot(diff,diff);
+ double det = Math.abs(A*C-B*B); // A*C-B*B = |Cross(M0,M1)|^2 >= 0
+
+ double tmp;
+
+ if (det >= FUZZ) {
+ // line segments are not parallel
+ E = -seg1dir.dot (diff); //-Dot(seg1dir,diff);
+ s = B*E-C*D;
+ t = B*D-A*E;
+
+ if (s >= 0) {
+ if (s <= det) {
+ if (t >= 0) {
+ if (t <= det) { // region 0 (interior)
+ // minimum at two interior points of 3D lines
+ double invDet = 1.0f/det;
+ s *= invDet;
+ t *= invDet;
+ if (seg0int != null) seg0int.scaleAdd (s, seg0dir, seg0start);
+ if (seg1int != null) seg1int.scaleAdd (t, seg1dir, seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(s*(A*s+B*t+2*D)+t*(B*s+C*t+2*E)+F);
+ }
+ else { // region 3 (side)
+ t = 1;
+ tmp = B+D;
+ if (tmp >= 0) {
+ s = 0;
+ if (seg0int != null) seg0int.set (seg0start);
+ if (seg1int != null) seg1int.set (seg1end);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(C+2*E+F);
+ }
+ else if (-tmp >= A) {
+ s = 1;
+ if (seg0int != null) seg0int.set (seg0end);
+ if (seg1int != null) seg1int.set (seg1end);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(A+C+F+2*(E+tmp));
+ }
+ else {
+ s = -tmp/A;
+ if (seg0int != null) seg0int.scaleAdd (s, seg0dir, seg0start);
+ if (seg1int != null) seg1int.set (seg1end);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(tmp*s+C+2*E+F);
+ }
+ }
+ }
+ else { // region 7 (side)
+ t = 0;
+ if (D >= 0) {
+ s = 0;
+ if (seg0int != null) seg0int.set (seg0start);
+ if (seg1int != null) seg1int.set (seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(F);
+ }
+ else if (-D >= A) {
+ s = 1;
+ if (seg0int != null) seg0int.set (seg0end);
+ if (seg1int != null) seg1int.set (seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(A+2*D+F);
+ }
+ else {
+ s = -D/A;
+ if (seg0int != null) seg0int.scaleAdd (s, seg0dir, seg0start);
+ if (seg1int != null) seg1int.set (seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(D*s+F);
+ }
+ }
+ }
+ else {
+ if (t >= 0) {
+ if (t <= det) { // region 1 (side)
+ s = 1;
+ tmp = B+E;
+ if (tmp >= 0) {
+ t = 0;
+ if (seg0int != null) seg0int.set (seg0end);
+ if (seg1int != null) seg1int.set (seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(A+2*D+F);
+ }
+ else if (-tmp >= C) {
+ t = 1;
+ if (seg0int != null) seg0int.set (seg0end);
+ if (seg1int != null) seg1int.set (seg1end);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(A+C+F+2*(D+tmp));
+ }
+ else {
+ t = -tmp/C;
+ if (seg0int != null) seg0int.set (seg0end);
+ if (seg1int != null) seg1int.scaleAdd (t, seg1dir, seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(tmp*t+A+2*D+F);
+ }
+ }
+ else { // region 2 (corner)
+ tmp = B+D;
+ if (-tmp <= A) {
+ t = 1;
+ if (tmp >= 0) {
+ s = 0;
+ if (seg0int != null) seg0int.set (seg0start);
+ if (seg1int != null) seg1int.set (seg1end);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(C+2*E+F);
+ }
+ else {
+ s = -tmp/A;
+ if (seg0int!=null) seg0int.scaleAdd (s, seg0dir, seg0start);
+ if (seg1int!=null) seg1int.set (seg1end);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(tmp*s+C+2*E+F);
+ }
+ }
+ else {
+ s = 1;
+ tmp = B+E;
+ if (tmp >= 0) {
+ t = 0;
+ if (seg0int!=null) seg0int.set (seg0end);
+ if (seg1int!=null) seg1int.set (seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(A+2*D+F);
+ }
+ else if (-tmp >= C) {
+ t = 1;
+ if (seg0int != null) seg0int.set (seg0end);
+ if (seg1int != null) seg1int.set (seg1end);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(A+C+F+2*(D+tmp));
+ }
+ else {
+ t = -tmp/C;
+ if (seg0int!=null) seg0int.set (seg0end);
+ if (seg1int!=null) seg1int.scaleAdd (t, seg1dir, seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(tmp*t+A+2*D+F);
+ }
+ }
+ }
+ }
+ else { // region 8 (corner)
+ if (-D < A) {
+ t = 0;
+ if (D >= 0) {
+ s = 0;
+ if (seg0int != null) seg0int.set (seg0start);
+ if (seg1int != null) seg1int.set (seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(F);
+ }
+ else {
+ s = -D/A;
+ if (seg0int != null) seg0int.scaleAdd (s, seg0dir, seg0start);
+ if (seg1int != null) seg1int.set (seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(D*s+F);
+ }
+ }
+ else {
+ s = 1;
+ tmp = B+E;
+ if (tmp >= 0) {
+ t = 0;
+ if (seg0int != null) seg0int.set (seg0end);
+ if (seg1int != null) seg1int.set (seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(A+2*D+F);
+ }
+ else if (-tmp >= C) {
+ t = 1;
+ if (seg0int != null) seg0int.set (seg0end);
+ if (seg1int != null) seg1int.set (seg1end);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(A+C+F+2*(D+tmp));
+ }
+ else {
+ t = -tmp/C;
+ if (seg0int != null) seg0int.set (seg0end);
+ if (seg1int != null) seg1int.scaleAdd (t, seg1dir, seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(tmp*t+A+2*D+F);
+ }
+ }
+ }
+ }
+ }
+ else {
+ if (t >= 0) {
+ if (t <= det) { // region 5 (side)
+ s = 0;
+ if (E >= 0) {
+ t = 0;
+ if (seg0int != null) seg0int.set (seg0start);
+ if (seg1int != null) seg1int.set (seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(F);
+ }
+ else if (-E >= C) {
+ t = 1;
+ if (seg0int != null) seg0int.set (seg0start);
+ if (seg1int != null) seg1int.set (seg1end);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(C+2*E+F);
+ }
+ else {
+ t = -E/C;
+ if (seg0int != null) seg0int.set (seg0start);
+ if (seg1int != null) seg1int.scaleAdd (t, seg1dir, seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(E*t+F);
+ }
+ }
+ else { // region 4 (corner)
+ tmp = B+D;
+ if (tmp < 0) {
+ t = 1;
+ if (-tmp >= A) {
+ s = 1;
+ if (seg0int != null) seg0int.set (seg0end);
+ if (seg1int != null) seg1int.set (seg1end);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(A+C+F+2*(E+tmp));
+ }
+ else {
+ s = -tmp/A;
+ if (seg0int != null) seg0int.scaleAdd (s, seg0dir, seg0start);
+ if (seg1int != null) seg1int.set (seg1end);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(tmp*s+C+2*E+F);
+ }
+ }
+ else {
+ s = 0;
+ if (E >= 0) {
+ t = 0;
+ if (seg0int != null) seg0int.set (seg0start);
+ if (seg1int != null) seg1int.set (seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(F);
+ }
+ else if (-E >= C) {
+ t = 1;
+ if (seg0int != null) seg0int.set (seg0start);
+ if (seg1int != null) seg1int.set (seg1end);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(C+2*E+F);
+ }
+ else {
+ t = -E/C;
+ if (seg0int != null) seg0int.set (seg0start);
+ if (seg1int != null) seg1int.scaleAdd (t, seg1dir, seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(E*t+F);
+ }
+ }
+ }
+ }
+ else { // region 6 (corner)
+ if (D < 0) {
+ t = 0;
+ if (-D >= A) {
+ s = 1;
+ if (seg0int != null) seg0int.set (seg0end);
+ if (seg1int != null) seg1int.set (seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(A+2*D+F);
+ }
+ else {
+ s = -D/A;
+ if (seg0int != null) seg0int.scaleAdd (s, seg0dir, seg0start);
+ if (seg1int != null) seg1int.set (seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(D*s+F);
+ }
+ }
+ else {
+ s = 0;
+ if (E >= 0) {
+ t = 0;
+ if (seg0int != null) seg0int.set (seg0start);
+ if (seg1int != null) seg1int.set (seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(F);
+ }
+ else if (-E >= C) {
+ t = 1;
+ if (seg0int != null) seg0int.set (seg0start);
+ if (seg1int != null) seg1int.set (seg1end);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(C+2*E+F);
+ }
+ else {
+ t = -E/C;
+ if (seg0int != null) seg0int.set (seg0start);
+ if (seg1int != null) seg1int.scaleAdd (t, seg1dir, seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(E*t+F);
+ }
+ }
+ }
+ }
+ }
+ else {
+ // line segments are parallel
+ if (B > 0) {
+ // direction vectors form an obtuse angle
+ if (D >= 0) {
+ s = 0;
+ t = 0;
+ if (seg0int != null) seg0int.set (seg0start);
+ if (seg1int != null) seg1int.set (seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(F);
+ }
+ else if (-D <= A) {
+ s = -D/A;
+ t = 0;
+ if (seg0int != null) seg0int.scaleAdd (s, seg0dir, seg0start);
+ if (seg1int != null) seg1int.set (seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(D*s+F);
+ }
+ else {
+ E = -seg1dir.dot (diff); //-Dot(seg1dir,diff);
+ s = 1;
+ tmp = A+D;
+ if (-tmp >= B) {
+ t = 1;
+ if (seg0int != null) seg0int.set (seg0end);
+ if (seg1int != null) seg1int.set (seg1end);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(A+C+F+2*(B+D+E));
+ }
+ else {
+ t = -tmp/B;
+ if (seg0int != null) seg0int.set (seg0end);
+ if (seg1int != null) seg1int.scaleAdd (t, seg1dir, seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(A+2*D+F+t*(C*t+2*(B+E)));
+ }
+ }
+ }
+ else {
+ // direction vectors form an acute angle
+ if (-D >= A) {
+ s = 1;
+ t = 0;
+ if (seg0int != null) seg0int.set (seg0end);
+ if (seg1int != null) seg1int.set (seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(A+2*D+F);
+ }
+ else if (D <= 0) {
+ s = -D/A;
+ t = 0;
+ if (seg0int != null) seg0int.scaleAdd (s, seg0dir, seg0start);
+ if (seg1int != null) seg1int.set (seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(D*s+F);
+ }
+ else {
+ E = -seg1dir.dot (diff); //-Dot(seg1dir,diff);
+ s = 0;
+ if (D >= -B) {
+ t = 1;
+ if (seg0int != null) seg0int.set (seg0start);
+ if (seg1int != null) seg1int.set (seg1end);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(C+2*E+F);
+ }
+ else {
+ t = -D/B;
+ if (seg0int != null) seg0int.set (seg0start);
+ if (seg1int != null) seg1int.scaleAdd (t, seg1dir, seg1start);
+ if (param != null) { param[0] = s; param[1] = t; }
+ return DIST(F+t*(2*E+C*t));
+ }
+ }
+ }
+ }
+ }
+}
+
+
+
+
diff --git a/src/classes/share/com/sun/j3d/internal/DoubleBufferWrapper.java b/src/classes/share/com/sun/j3d/internal/DoubleBufferWrapper.java
new file mode 100755
index 0000000..c6f011d
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/internal/DoubleBufferWrapper.java
@@ -0,0 +1,153 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.internal;
+
+import javax.media.j3d.J3DBuffer;
+import java.nio.DoubleBuffer;
+
+/**
+ * NIO Buffers are new in Java 1.4 but we need to run on 1.3
+ * as well, so this class was created to hide the NIO classes
+ * from non-1.4 Java 3D users.
+ *
+ * <p>
+ * NOTE: We no longer need to support JDK 1.3 as of the Java 3D 1.3.2
+ * community source release on java.net. We should be able to get rid
+ * of this class.
+ */
+
+public class DoubleBufferWrapper extends BufferWrapper {
+
+ private DoubleBuffer buffer = null;
+
+ /**
+ * Constructor initializes buffer with a
+ * java.nio.DoubleBuffer object.
+ */
+ public DoubleBufferWrapper(DoubleBuffer buffer) {
+ this.buffer = buffer;
+ }
+
+ /**
+ * Constructor initializes buffer with a
+ * javax.media.j3d.J3DBuffer object.
+ */
+ public DoubleBufferWrapper(J3DBuffer b) {
+ buffer = (DoubleBuffer)(b.getBuffer());
+ }
+
+ /**
+ * Returns the java.nio.Buffer contained within this
+ * DoubleBufferWrapper.
+ */
+ public java.nio.Buffer getBuffer() {
+ return this.buffer;
+ }
+
+ // Wrapper for all relevant DoubleBuffer methods.
+
+ /**
+ * @return A boolean indicating whether the java.nio.Buffer
+ * object contained within this DoubleBuffer is direct or
+ * indirect.
+ */
+ public boolean isDirect() {
+ return buffer.isDirect();
+ }
+
+ /**
+ * Reads the double at this buffer's current position,
+ * and then increments the position.
+ */
+ public double get() {
+ return buffer.get();
+ }
+
+ /**
+ * Reads the double at the given offset into the buffer.
+ */
+ public double get(int index) {
+ return buffer.get(index);
+ }
+
+ /**
+ * Bulk <i>get</i> method. Transfers <code>dst.length</code>
+ * doubles from
+ * the buffer to the destination array and increments the
+ * buffer's position by <code>dst.length</code>.
+ */
+ public DoubleBufferWrapper get(double[] dst) {
+ buffer.get(dst);
+ return this;
+ }
+
+ /**
+ * Bulk <i>get</i> method. Transfers <i>length</i> doubles
+ * from the buffer starting at position <i>offset</i> into
+ * the destination array.
+ */
+ public DoubleBufferWrapper get(double[] dst, int offset, int length){
+ buffer.get(dst, offset, length);
+ return this;
+ }
+
+ /**
+ * Bulk <i>put</i> method. Transfers <code>src.length</code>
+ * doubles into the buffer at the current position.
+ */
+ public DoubleBufferWrapper put(double[] src) {
+ buffer.put(src);
+ return this;
+ }
+
+ /**
+ * Creates and returns a J3DBuffer object containing the
+ * buffer in this DoubleBufferWrapper object.
+ */
+ public J3DBuffer getJ3DBuffer() {
+ return new J3DBuffer( buffer );
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/internal/FastVector.java b/src/classes/share/com/sun/j3d/internal/FastVector.java
new file mode 100644
index 0000000..b3ed4c2
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/internal/FastVector.java
@@ -0,0 +1,143 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.internal;
+
+/**
+ * The FastVector object is a growable array of ints. It's much faster
+ * than the Java Vector class because it isn't synchronized. This class
+ * was created because it is needed in several places by graphics
+ * utilities.
+ */
+public class FastVector {
+
+ private int data[];
+ private int capacity;
+ private int increment;
+ private int size;
+
+ /**
+ * Add an element to the end of the array.
+ */
+ public void addElement(int element)
+ {
+ if (size >= capacity) {
+ capacity += (increment == 0) ? capacity : increment;
+ int newData[] = new int[capacity];
+ System.arraycopy(data, 0, newData, 0, size);
+ data = newData;
+ }
+ data[size++] = element;
+ } // End of addElement
+
+
+
+ /**
+ * Get number of ints currently stored in the array;
+ */
+ public int getSize()
+ {
+ return size;
+ } // End of getSize
+
+
+
+ /**
+ * Get access to array data
+ */
+ public int[] getData()
+ {
+ return data;
+ } // End of getData
+
+
+
+ /**
+ * Constructor.
+ * @param initialCapacity Number of ints the object can hold
+ * without reallocating the array.
+ * @param capacityIncrement Once the array has grown beyond
+ * its capacity, how much larger the reallocated array should be.
+ */
+ public FastVector(int initialCapacity, int capacityIncrement)
+ {
+ data = new int[initialCapacity];
+ capacity = initialCapacity;
+ increment = capacityIncrement;
+ size = 0;
+ } // End of FastVector(int, int)
+
+
+
+ /**
+ * Constructor.
+ * When the array runs out of space, its size is doubled.
+ * @param initialCapacity Number of ints the object can hold
+ * without reallocating the array.
+ */
+ public FastVector(int initialCapacity)
+ {
+ data = new int[initialCapacity];
+ capacity = initialCapacity;
+ increment = 0;
+ size = 0;
+ } // End of FastVector(int)
+
+
+
+ /**
+ * Constructor.
+ * The array is constructed with initial capacity of one integer.
+ * When the array runs out of space, its size is doubled.
+ */
+ public FastVector()
+ {
+ data = new int[1];
+ capacity = 1;
+ increment = 0;
+ size = 0;
+ } // End of FastVector()
+} // End of class FastVector
+
+// End of file FastVector.java
diff --git a/src/classes/share/com/sun/j3d/internal/FloatBufferWrapper.java b/src/classes/share/com/sun/j3d/internal/FloatBufferWrapper.java
new file mode 100755
index 0000000..8868901
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/internal/FloatBufferWrapper.java
@@ -0,0 +1,152 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.internal;
+
+import javax.media.j3d.J3DBuffer;
+import java.nio.FloatBuffer;
+
+/**
+ * NIO Buffers are new in Java 1.4 but we need to run on 1.3
+ * as well, so this class was created to hide the NIO classes
+ * from non-1.4 Java 3D users.
+ *
+ * <p>
+ * NOTE: We no longer need to support JDK 1.3 as of the Java 3D 1.3.2
+ * community source release on java.net. We should be able to get rid
+ * of this class.
+ */
+
+public class FloatBufferWrapper extends BufferWrapper {
+
+ private FloatBuffer buffer = null;
+
+ /**
+ * Constructor initializes buffer with a
+ * java.nio.FloatBuffer object.
+ */
+ public FloatBufferWrapper(FloatBuffer buffer) {
+ this.buffer = buffer;
+ }
+
+ /**
+ * Constructor initializes buffer with a
+ * javax.media.j3d.J3DBuffer object.
+ */
+ public FloatBufferWrapper(javax.media.j3d.J3DBuffer b) {
+ this.buffer = (FloatBuffer)(b.getBuffer());
+ }
+
+ /**
+ * Returns the java.nio.Buffer contained within this
+ * FloatBufferWrapper.
+ */
+ public java.nio.Buffer getBuffer() {
+ return this.buffer;
+ }
+
+ // Wrapper for all relevant FloatBuffer methods.
+
+ /**
+ * @return A boolean indicating whether the java.nio.Buffer
+ * object contained within this FloatBuffer is direct or
+ * indirect.
+ */
+ public boolean isDirect() {
+ return buffer.isDirect();
+ }
+
+ /**
+ * Reads the float at this buffer's current position,
+ * and then increments the position.
+ */
+ public float get() {
+ return buffer.get();
+ }
+
+ /**
+ * Reads the float at the given offset into the buffer.
+ */
+ public float get(int index) {
+ return buffer.get(index);
+ }
+
+ /**
+ * Bulk <i>get</i> method. Transfers <code>dst.length</code>
+ * floats from
+ * the buffer to the destination array and increments the
+ * buffer's position by <code>dst.length</code>.
+ */
+ public FloatBufferWrapper get(float[] dst) {
+ buffer.get(dst);
+ return this;
+ }
+
+ /**
+ * Bulk <i>get</i> method. Transfers <i>length</i> floats
+ * from the buffer starting at position <i>offset</i> into
+ * the destination array.
+ */
+ public FloatBufferWrapper get(float[] dst, int offset, int length){
+ buffer.get(dst, offset, length);
+ return this;
+ }
+
+ /**
+ * Bulk <i>put</i> method. Transfers <code>src.length</code>
+ * floats into the buffer at the current position.
+ */
+ public FloatBufferWrapper put(float[] src) {
+ buffer.put(src);
+ return this;
+ }
+
+ /**
+ * Creates and returns a J3DBuffer object containing the
+ * buffer in this FloatBufferWrapper object.
+ */
+ public J3DBuffer getJ3DBuffer() {
+ return new J3DBuffer( buffer );
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/internal/J3dUtilsI18N.java b/src/classes/share/com/sun/j3d/internal/J3dUtilsI18N.java
new file mode 100644
index 0000000..a61c43a
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/internal/J3dUtilsI18N.java
@@ -0,0 +1,63 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.internal;
+
+import java.io.*;
+import java.util.*;
+
+
+public class J3dUtilsI18N {
+ static public String getString(String key) {
+ String s;
+ try {
+ s = (String) ResourceBundle.getBundle("com.sun.j3d.ExceptionStrings").getString(key);
+ }
+ catch (MissingResourceException e) {
+ System.err.println("J3dUtilsI18N: Error looking up: " + key);
+ s = key;
+ }
+ return s;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/internal/UtilFreelistManager.java b/src/classes/share/com/sun/j3d/internal/UtilFreelistManager.java
new file mode 100644
index 0000000..457968a
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/internal/UtilFreelistManager.java
@@ -0,0 +1,98 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.internal;
+
+
+public class UtilFreelistManager {
+
+ private static final boolean DEBUG = false;
+
+ // constants that represent the freelists managed by the Manager
+ public static final int VECTOR3D = 0;
+ public static final int POINT3D = 1;
+ public static final int PICKRESULT = 2;
+ public static final int MAXINT = 2;
+
+ // what list we are going to shrink next
+ private static int currlist = 0;
+
+ // the freelists managed by the manager
+ public static UtilMemoryFreelist vector3dFreelist = new UtilMemoryFreelist("javax.vecmath.Vector3d");
+ public static UtilMemoryFreelist point3dFreelist = new UtilMemoryFreelist("javax.vecmath.Point3d");
+ public static UtilMemoryFreelist pickResultFreelist = new UtilMemoryFreelist("com.sun.j3d.utils.picking.PickResult");
+
+
+// static MemoryFreeList[] freelist = new MemoryFreeList[MAXINT+1];
+
+// static void createFreeLists() {
+// freelist[VECTOR3D] = new MemoryFreeList("javax.vecmath.Vector3d");
+// freelist[POINT3D] = new MemoryFreeList("javax.vecmath.Point3d");
+// }
+
+
+// // see if the current list can be shrunk
+// static void manageLists() {
+// // System.out.println("manageLists");
+// if (freelist[currlist] != null) {
+// freelist[currlist].shrink();
+// }
+
+// currlist++;
+// if (currlist > MAXINT) currlist = 0;
+// }
+
+// // return the freelist specified by the list param
+// static MemoryFreeList getFreeList(int list) {
+// if (list < 0 || list > MAXINT) {
+// if (DEBUG) System.out.println("illegal list");
+// return null;
+// }
+// else {
+// return freelist[list];
+// }
+// }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/internal/UtilMemoryFreelist.java b/src/classes/share/com/sun/j3d/internal/UtilMemoryFreelist.java
new file mode 100644
index 0000000..3f559b7
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/internal/UtilMemoryFreelist.java
@@ -0,0 +1,292 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.internal;
+
+import java.util.*;
+
+// this class must be synchronized because different threads may try to access
+// the freelists
+public class UtilMemoryFreelist { // extends AbstractList {
+
+ // never go smaller than the initial capacity
+ ArrayList elementData = null;
+ int size = 0;
+ int currBlockSize = 10;
+ Object[] currBlock = null;
+ int currBlockIndex = 0;
+ int spaceUsed = 0;
+ int numBlocks = 0;
+ int capacity = 0;
+ int minBlockSize = 0;
+ boolean justShrunk = false;
+ int initcap = 10;
+
+ // the minimum size since the last shrink
+ int minSize = 0;
+
+ Class c = null;
+
+ public UtilMemoryFreelist(String className) {
+ this(className, 10);
+ }
+
+ public UtilMemoryFreelist(String className, int initialCapacity) {
+ if (initialCapacity < 0) {
+ throw new IllegalArgumentException ("Illegal Capacity: " +
+ initialCapacity);
+ }
+
+ try {
+ c = Class.forName(className);
+ }
+ catch (Exception e) {
+// System.out.println(e);
+ }
+
+ initcap = initialCapacity;
+ currBlockSize = initialCapacity;
+ minBlockSize = currBlockSize;
+ elementData = new ArrayList();
+ // add the first block of memory to the arraylist
+ currBlock = new Object[currBlockSize];
+ elementData.add(currBlock);
+ numBlocks++;
+ capacity += currBlockSize;
+ }
+
+ public UtilMemoryFreelist(String className, Collection collection) {
+ try {
+ c = Class.forName(className);
+ }
+ catch (Exception e) {
+// System.out.println(e);
+ }
+ size = collection.size();
+ initcap = size;
+ currBlockSize = size;
+ minBlockSize = currBlockSize;
+ elementData = new ArrayList();
+ currBlock = new Object[currBlockSize];
+ collection.toArray(currBlock);
+ elementData.add(currBlock);
+ numBlocks++;
+ capacity += currBlockSize;
+ spaceUsed = size;
+ }
+
+ public synchronized int size() {
+ return size;
+ }
+
+
+ public synchronized boolean add(Object o) {
+ if (justShrunk) {
+ // empty some space out in the current block instead of
+ // adding this message
+ if ((currBlockSize/2) < spaceUsed) {
+ size -= (spaceUsed - (currBlockSize/2));
+ spaceUsed = (currBlockSize/2);
+ Arrays.fill(currBlock, spaceUsed, currBlockSize-1, null);
+ }
+ justShrunk = false;
+ return false;
+ }
+ else {
+ ensureCapacity(size+1);
+
+ // check to see if the whole block is used and if so, reset the
+ // current block
+// System.out.println("spaceUsed = " + spaceUsed + " currBlockSize = " +
+// currBlockSize + " currBlockIndex = " +
+// currBlockIndex + " currBlock = " + currBlock);
+ if ((currBlockIndex == -1) || (spaceUsed >= currBlockSize)) {
+ currBlockIndex++;
+ currBlock = (Object[])elementData.get(currBlockIndex);
+ currBlockSize = currBlock.length;
+ spaceUsed = 0;
+ }
+ int index = spaceUsed++;
+ currBlock[index] = o;
+ size++;
+
+ return true;
+ }
+ }
+
+ private synchronized Object removeLastElement() {
+// System.out.println("removeLastElement: size = " + size);
+ int index = --spaceUsed;
+// System.out.println("index = " + index);
+ Object elm = currBlock[index];
+ currBlock[index] = null;
+ size--;
+
+ // see if this block is empty now, and if it is set the previous
+ // block to the current block
+ if (spaceUsed == 0) {
+ currBlockIndex--;
+ if (currBlockIndex < 0) {
+ currBlock = null;
+ currBlockSize = 0;
+ }
+ else {
+ currBlock = (Object[])elementData.get(currBlockIndex);
+ currBlockSize = currBlock.length;
+ }
+ spaceUsed = currBlockSize;
+ }
+
+ return elm;
+ }
+
+
+ public synchronized void shrink() {
+// System.out.println("shrink size = " + size + " minSize = " +
+// minSize);
+ if ((minSize > minBlockSize) && (numBlocks > 1)) {
+ justShrunk = true;
+
+// System.out.println("removing a block");
+// Runtime r = Runtime.getRuntime();
+// r.gc();
+// System.out.println("numBlocks = " + numBlocks + " size = " + size);
+// System.out.println("free memory before shrink: " + r.freeMemory());
+
+ // remove the last block
+ Object[] block = (Object[])elementData.remove(numBlocks-1);
+ numBlocks--;
+ capacity -= block.length;
+
+ // we only need to do this if the block removed was the current
+ // block. otherwise we just removed a null block.
+ if (numBlocks == currBlockIndex) {
+ size -= spaceUsed;
+ // set the current block to the last one
+ currBlockIndex = numBlocks-1;
+ currBlock = (Object[])elementData.get(currBlockIndex);
+ currBlockSize = currBlock.length;
+
+ spaceUsed = currBlockSize;
+
+ }
+
+// r.gc();
+// System.out.println("free memory after shrink: " + r.freeMemory());
+// System.out.println("numBlocks = " + numBlocks + " size = " + size);
+ }
+ else {
+ justShrunk = false;
+ }
+ minSize = size;
+ }
+
+ public synchronized void ensureCapacity(int minCapacity) {
+// System.out.println("ensureCapacity: size = " + size + " capacity: " +
+// elementData.length);
+// System.out.println("minCapacity = " + minCapacity + " capacity = "
+// + capacity);
+
+ if (minCapacity > capacity) {
+// System.out.println("adding a block: numBlocks = " + numBlocks);
+ int lastBlockSize =
+ ((Object[])elementData.get(numBlocks-1)).length;
+ int prevBlockSize = 0;
+ if (numBlocks > 1) {
+ prevBlockSize =
+ ((Object[])elementData.get(numBlocks-2)).length;
+ }
+ currBlockSize = lastBlockSize + prevBlockSize;
+ currBlock = new Object[currBlockSize];
+ elementData.add(currBlock);
+ numBlocks++;
+ currBlockIndex++;
+ capacity += currBlockSize;
+ // there is nothing used in this block yet
+ spaceUsed = 0;
+ }
+ }
+
+ synchronized void rangeCheck(int index) {
+ if (index >= size || index < 0) {
+ throw new IndexOutOfBoundsException("Index: " + index +
+ ", Size: " + size);
+ }
+ }
+
+ public synchronized void clear() {
+// System.out.println("clear");
+ elementData.clear();
+
+ // put an empty block in
+ currBlockSize = initcap;
+ minBlockSize = currBlockSize;
+ currBlock = new Object[currBlockSize];
+ elementData.add(currBlock);
+ numBlocks = 1;
+ capacity = currBlockSize;
+ spaceUsed = 0;
+ size = 0;
+ currBlockIndex = 0;
+ justShrunk = false;
+ }
+
+ public synchronized Object getObject() {
+ if (size > 0) {
+ return removeLastElement();
+ }
+ else {
+ try {
+ return c.newInstance();
+ }
+ catch (Exception e) {
+// System.out.println("caught exception");
+// System.out.print(e);
+ return null;
+ }
+ }
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/loaders/IncorrectFormatException.java b/src/classes/share/com/sun/j3d/loaders/IncorrectFormatException.java
new file mode 100644
index 0000000..3ab53d8
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/IncorrectFormatException.java
@@ -0,0 +1,62 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders;
+
+
+/**
+ * Exception used to indicate that a file of the incorrect
+ * type was passed to a loader.
+ */
+public class IncorrectFormatException extends RuntimeException {
+
+ public IncorrectFormatException() {
+ super();
+ }
+
+ public IncorrectFormatException(String s) {
+ super(s);
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/loaders/Loader.java b/src/classes/share/com/sun/j3d/loaders/Loader.java
new file mode 100644
index 0000000..aeab387
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/Loader.java
@@ -0,0 +1,176 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders;
+
+import java.net.URL;
+import java.io.Reader;
+import java.io.FileNotFoundException;
+
+/**
+ * The Loader interface is used to specify the location
+ * and elements of a file format to load.
+ * The interface is used to give loaders of various
+ * file formats a common public interface. Ideally
+ * the Scene interface will be implemented to give
+ * the user a consistent interface to extract the
+ * data.
+ *
+ * @see com.sun.j3d.loaders.Scene
+ */
+public interface Loader {
+
+ // These are the values to be used in constructing the
+ // load flags for the loader. Users should OR the selected
+ // values together to construct an aggregate flag integer
+ // (see the setFlags() method). Users wishing to load all
+ // data in a file should use the LOAD_ALL specifier.
+
+ /** This flag enables the loading of light objects into the scene.*/
+ public static final int LOAD_LIGHT_NODES = 1;
+
+ /** This flag enables the loading of fog objects into the scene.*/
+ public static final int LOAD_FOG_NODES = 2;
+
+ /** This flag enables the loading of background objects into the scene.*/
+ public static final int LOAD_BACKGROUND_NODES = 4;
+
+ /** This flag enables the loading of behaviors into the scene.*/
+ public static final int LOAD_BEHAVIOR_NODES = 8;
+
+ /** This flag enables the loading of view (camera) objects into
+ * the scene.*/
+ public static final int LOAD_VIEW_GROUPS = 16;
+
+ /** This flag enables the loading of sound objects into the scene.*/
+ public static final int LOAD_SOUND_NODES = 32;
+
+ /** This flag enables the loading of all objects into the scene.*/
+ public static final int LOAD_ALL = 0xffffffff;
+
+
+ // Loading methods
+
+ /**
+ * This method loads the named file and returns the Scene
+ * containing the scene. Any data files referenced by this
+ * file should be located in the same place as the named file;
+ * otherwise users should specify an alternate base path with
+ * the setBasePath(String) method.
+ */
+ public Scene load(String fileName) throws FileNotFoundException,
+ IncorrectFormatException, ParsingErrorException;
+
+ /**
+ * This method loads the named file and returns the Scene
+ * containing the scene. Any data files referenced by the Reader
+ * should be located in the same place as the named file; otherwise,
+ * users should specify an alternate base path with the setBaseUrl(URL)
+ * method.
+ */
+ public Scene load(URL url) throws FileNotFoundException,
+ IncorrectFormatException, ParsingErrorException;
+
+ /**
+ * This method loads the Reader and returns the Scene
+ * containing the scene. Any data files referenced by the Reader should
+ * be located in the user's current working directory.
+ */
+ public Scene load(Reader reader)
+ throws FileNotFoundException, IncorrectFormatException,
+ ParsingErrorException;
+
+
+ // Variable get/set methods
+
+ /**
+ * This method sets the base URL name for data files associated with
+ * the file passed into the load(URL) method.
+ * The basePath should be null by default, which is an indicator
+ * to the loader that it should look for any associated files starting
+ * from the same directory as the file passed into the load(URL) method.
+ */
+ public void setBaseUrl(URL url);
+
+ /**
+ * This method sets the base path name for data files associated with
+ * the file passed into the load(String) method.
+ * The basePath should be null by default, which is an indicator
+ * to the loader that it should look for any associated files starting
+ * from the same directory as the file passed into the load(String)
+ * method.
+ */
+ public void setBasePath(String pathName);
+
+ /**
+ * Returns the current base URL setting. By default this is null,
+ * implying the loader should look for associated files starting
+ * from the same directory as the file passed into the load(URL) method.
+ */
+ public URL getBaseUrl();
+
+ /**
+ * Returns the current base path setting. By default this is null,
+ * implying the loader should look for associated files starting
+ * from the same directory as the file passed into the load(String)
+ * method.
+ */
+ public String getBasePath();
+
+ /**
+ * This method sets the load flags for the file. The flags should
+ * equal 0 by default (which tells the loader to only load geometry).
+ * To enable the loading of any particular scene elements, pass
+ * in a logical OR of the LOAD values specified above.
+ */
+ public void setFlags(int flags);
+
+ /**
+ * Returns the current loading flags setting.
+ */
+ public int getFlags();
+}
+
+
+
diff --git a/src/classes/share/com/sun/j3d/loaders/LoaderBase.java b/src/classes/share/com/sun/j3d/loaders/LoaderBase.java
new file mode 100644
index 0000000..ba2d92e
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/LoaderBase.java
@@ -0,0 +1,147 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders;
+
+import java.net.URL;
+import java.io.Reader;
+
+/**
+ * This class implements the Loader interface. To use
+ * a file loader would extend this class.
+ */
+public abstract class LoaderBase implements Loader {
+
+ /** Stores the types of objects that the user wishes to load.*/
+ protected int loadFlags = 0;
+
+ /** Stores the baseUrl for data files associated with the URL
+ * passed into load(URL).*/
+ protected URL baseUrl = null;
+
+ /** Stores the basePath for data files associated with the file
+ * passed into load(String).*/
+ protected String basePath = null;
+
+ // Constructors
+
+ /**
+ * Constructs a Loader with default values for all variables.
+ */
+ public LoaderBase() {
+ }
+
+ /**
+ * Constructs a Loader with the specified flags word.
+ */
+ public LoaderBase(int flags) {
+ loadFlags = flags;
+ }
+
+
+ // Variable get/set methods
+
+ /**
+ * This method sets the base URL name for data files associated with
+ * the file. The baseUrl should be null by default, which is an indicator
+ * to the loader that it should look for any associated files starting
+ * from the same place as the URL passed into the load(URL) method.
+ * Note: Users of setBaseUrl() would then use load(URL)
+ * as opposed to load(String).
+ */
+ public void setBaseUrl(URL url) {
+ baseUrl = url;
+ }
+
+ /**
+ * This method sets the base path name for data files associated with
+ * the file. The basePath should be null by default, which is an indicator
+ * to the loader that it should look for any associated files starting
+ * from the same directory as the file passed into the load(String)
+ * method.
+ * Note: Users of setBasePath() would then use load(String)
+ * as opposed to load(URL).
+ */
+ public void setBasePath(String pathName) {
+ basePath = pathName;
+ }
+
+ /**
+ * Returns the current base URL setting.
+ */
+ public URL getBaseUrl() {
+ return baseUrl;
+ }
+
+ /**
+ * Returns the current base path setting.
+ */
+ public String getBasePath() {
+ return basePath;
+ }
+
+ /**
+ * This method sets the load flags for the file. The flags should
+ * equal 0 by default (which tells the loader to only load geometry).
+ */
+ public void setFlags(int flags) {
+ loadFlags = flags;
+ }
+
+ /**
+ * Returns the current loading flags setting.
+ */
+ public int getFlags() {
+ return loadFlags;
+ }
+
+}
+
+
+
+
+
+
+
+
diff --git a/src/classes/share/com/sun/j3d/loaders/ParsingErrorException.java b/src/classes/share/com/sun/j3d/loaders/ParsingErrorException.java
new file mode 100644
index 0000000..7d94002
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/ParsingErrorException.java
@@ -0,0 +1,62 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders;
+
+
+/**
+ * Exception used to indicate that the loader encountered
+ * a problem parsing the specified file.
+ */
+public class ParsingErrorException extends RuntimeException {
+
+ public ParsingErrorException() {
+ super();
+ }
+
+ public ParsingErrorException(String s) {
+ super(s);
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/loaders/Scene.java b/src/classes/share/com/sun/j3d/loaders/Scene.java
new file mode 100644
index 0000000..34f3399
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/Scene.java
@@ -0,0 +1,148 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders;
+
+import java.util.Hashtable;
+
+import javax.media.j3d.Behavior;
+import javax.media.j3d.BranchGroup;
+import javax.media.j3d.TransformGroup;
+import javax.media.j3d.Light;
+import javax.media.j3d.Background;
+import javax.media.j3d.Fog;
+import javax.media.j3d.Sound;
+
+
+/**
+ * The Scene interface is a set of methods used to extract
+ * Java 3D scene graph information from a file loader utility.
+ * The interface is used to give loaders of various
+ * file formats a common public interface.
+ */
+public interface Scene {
+
+
+ /**
+ * This method returns the BranchGroup containing the overall
+ * scene loaded by the loader. All enabled items will be loaded
+ * into this scene except for Behaviors (the Behavior group must be
+ * retrieved separately so that users can choose whether and when
+ * to activate behaviors).
+ */
+ public BranchGroup getSceneGroup();
+
+ /**
+ * This method returns an array of all View Groups defined in the file.
+ * Each View Group is a TransformGroup that is already placed within
+ * the scene that is returned in the getSceneGroup() call. This
+ * TransformGroup holds the position/orientation of the view
+ * as defined by the file. A user might request these references to
+ * the groups in order to look at the data stored there or
+ * to place ViewPlatforms within these groups and allow the
+ * View to activate these ViewPlatforms so that the user would
+ * see the scene from the viewpoints defined in the file.
+ */
+ public TransformGroup[] getViewGroups();
+
+ /**
+ * This method returns an array of floats with the horizontal field
+ * of view. The entries in the array will correspond to those in the
+ * array returned by the method getViewGroups. The entries from these
+ * two arrays together provide all the information needed to recreate
+ * the viewing parameters associated with a scene graph.
+ */
+ public float[] getHorizontalFOVs();
+
+ /**
+ * This method returns an array of all Lights defined in the file.
+ * If no lights are defined, null is returned.
+ */
+ public Light[] getLightNodes();
+
+ /**
+ * This method returns a Hashtable which contains a list of all named
+ * objects in the file and their associated scene graph objects. The
+ * naming scheme for file objects is file-type dependent, but may include
+ * such names as the DEF names of Vrml or filenames of objects (as
+ * in Lightwave 3D). If no named objects are defined, null is returned.
+ */
+ public Hashtable getNamedObjects();
+
+ /**
+ * This method returns an array of all Background nodes defined in the
+ * file. IF no Background nodes are defined, null is returned.
+ */
+ public Background[] getBackgroundNodes();
+
+ /**
+ * This method returns an array of all Fog nodes defined in the
+ * file. If no fog nodes are defined, null is returned.
+ */
+ public Fog[] getFogNodes();
+
+ /**
+ * This method returns an array of all the behavior nodes
+ * in the scene. If no Behavior nodes are defined, null is returned.
+ */
+ public Behavior[] getBehaviorNodes();
+
+ /**
+ * This method returns an array of all of the Sound nodes defined
+ * in the file. If no Sound nodes are defined, null is returned.
+ */
+ public Sound[] getSoundNodes();
+
+ /**
+ * This method returns the text description of the file. If no
+ * such description exists, this method should return null.
+ */
+ public String getDescription();
+
+}
+
+
+
+
+
diff --git a/src/classes/share/com/sun/j3d/loaders/SceneBase.java b/src/classes/share/com/sun/j3d/loaders/SceneBase.java
new file mode 100644
index 0000000..05f6413
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/SceneBase.java
@@ -0,0 +1,311 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders;
+
+import java.lang.Float;
+
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.Enumeration;
+
+import javax.media.j3d.Behavior;
+import javax.media.j3d.BranchGroup;
+import javax.media.j3d.TransformGroup;
+import javax.media.j3d.Light;
+import javax.media.j3d.Background;
+import javax.media.j3d.Fog;
+import javax.media.j3d.Sound;
+
+
+/**
+ * This class implements the Scene interface and extends it to incorporate
+ * utilities that could be used by loaders. There should be little need
+ * for future loaders to subclass this, or to implement Scene directly,
+ * as the functionality of a SceneBase is fairly straightforward. This
+ * class is responsible for both the storage and retrieval of data from
+ * the Scene. The storage methods (used only by Loader writers) are all
+ * of the add* routines. The retrieval methods (used primarily by Loader
+ * users) are all of the get* routines.
+ */
+public class SceneBase implements Scene {
+
+ BranchGroup sceneGroup = null;
+ BranchGroup behaviorGroup = null;
+ Hashtable namedObjects = new Hashtable();
+ String description = null;
+
+ Vector viewVector = new Vector();
+ Vector hfovVector = new Vector();
+ Vector behaviorVector = new Vector();
+ Vector lightVector = new Vector();
+ Vector fogVector = new Vector();
+ Vector backgroundVector = new Vector();
+ Vector soundVector = new Vector();
+
+ // Add methods
+
+ /**
+ * Sets the sceneGroup to be the group that is passed in.
+ */
+ public void setSceneGroup(BranchGroup scene) {
+ sceneGroup = scene;
+ }
+
+ /**
+ * Adds the given group to the list of view groups.
+ */
+ public void addViewGroup(TransformGroup tg) {
+ viewVector.addElement(tg);
+ }
+
+ /**
+ * Adds the given field of view value to the list of field of view values.
+ */
+ public void addHorizontalFOV(float hfov) {
+ hfovVector.addElement(new Float(hfov));
+ }
+
+ /**
+ * Adds the given behavior to a list of behaviors
+ */
+ public void addBehaviorNode(Behavior b) {
+ behaviorVector.addElement(b);
+ }
+
+ /**
+ * Adds the given Light node to the list of lights.
+ */
+ public void addLightNode(Light light) {
+ lightVector.addElement(light);
+ }
+
+ /**
+ * Adds the given Background node to the list of backgrounds.
+ */
+ public void addBackgroundNode(Background background) {
+ backgroundVector.addElement(background);
+ }
+
+ /**
+ * Adds the given Sound node to the list of sounds.
+ */
+ public void addSoundNode(Sound sound) {
+ soundVector.addElement(sound);
+ }
+
+ /**
+ * Adds the given Fog node to the list of fog nodes.
+ */
+ public void addFogNode(Fog fog) {
+ fogVector.addElement(fog);
+ }
+
+ /**
+ * Sets the text description of the scene to the passed in String.
+ */
+ public void addDescription(String descriptionString) {
+ description = descriptionString;
+ }
+
+ /**
+ * Adds the given String/Object pair to the table of named objects.
+ */
+ public void addNamedObject(String name, Object object) {
+ if (namedObjects.get(name) == null)
+ namedObjects.put(name, object);
+ else {
+ // key already exists - append a unique integer to end of name
+ int nameIndex = 1;
+ boolean done = false;
+ while (!done) {
+ // Iterate starting from "[1]" until we find a unique key
+ String tempName = name + "[" + nameIndex + "]";
+ if (namedObjects.get(tempName) == null) {
+ namedObjects.put(tempName, object);
+ done = true;
+ }
+ nameIndex++;
+ }
+ }
+ }
+
+ /**
+ * This method returns the BranchGroup containing the overall
+ * scene loaded by the loader.
+ */
+ public BranchGroup getSceneGroup() {
+ return sceneGroup;
+ }
+
+
+ /**
+ * This method returns an array of all View Groups defined in the file.
+ * A View Group is defined as a TransformGroup which contains a
+ * ViewPlatform. The TransformGroup holds the position/orientation
+ * information for the given ViewPlatform and the ViewPlatform
+ * holds an view-specific information, such as Field of View.
+ */
+ public TransformGroup[] getViewGroups() {
+ if (viewVector.isEmpty())
+ return null;
+ TransformGroup[] viewGroups = new TransformGroup[viewVector.size()];
+ viewVector.copyInto(viewGroups);
+ return viewGroups;
+ }
+
+ /**
+ * This method returns an array of floats that contains the horizontal
+ * field of view values for each corresponding entry in the array of
+ * view groups returned by the method getViewGroups.
+ */
+ public float[] getHorizontalFOVs() {
+ if (hfovVector.isEmpty())
+ return null;
+
+ int arraySize = hfovVector.size();
+ float[] hfovs = new float[arraySize];
+ Float[] tmpFovs = new Float[hfovVector.size()];
+ hfovVector.copyInto(tmpFovs);
+
+ // copy to array of floats and delete Floats
+ for (int i=0; i<arraySize; i++) {
+ hfovs[i] = tmpFovs[i].floatValue();
+ tmpFovs[i] = null;
+ }
+ return hfovs;
+ }
+
+
+ /**
+ * This method returns an array of all Lights defined in the file.
+ */
+ public Light[] getLightNodes() {
+ if (lightVector.isEmpty())
+ return null;
+ Light[] lightNodes = new Light[lightVector.size()];
+ lightVector.copyInto(lightNodes);
+ return lightNodes;
+ }
+
+
+ /**
+ * This method returns a Hashtable which contains a list of all named
+ * objects in the file and their associated scene graph objects. The
+ * naming scheme for file objects is file-type dependent, but may include
+ * such names as the DEF names of Vrml or filenames of subjects (as
+ * in Lightwave 3D).
+ */
+ public Hashtable getNamedObjects() {
+ return namedObjects;
+ }
+
+
+ /**
+ * This method returns an array of all Background nodes defined in the
+ * file.
+ */
+ public Background[] getBackgroundNodes() {
+ if (backgroundVector.isEmpty())
+ return null;
+ Background[] backgroundNodes = new Background[backgroundVector.size()];
+ backgroundVector.copyInto(backgroundNodes);
+ return backgroundNodes;
+ }
+
+
+ /**
+ * This method returns an array of all Fog nodes defined in the
+ * file.
+ */
+ public Fog[] getFogNodes() {
+ if (fogVector.isEmpty())
+ return null;
+ Fog[] fogNodes = new Fog[fogVector.size()];
+ fogVector.copyInto(fogNodes);
+ return fogNodes;
+ }
+
+
+ /**
+ * This method returns a group containing all of the Behavior nodes
+ * in the scene.
+ */
+ public Behavior[] getBehaviorNodes() {
+ if (behaviorVector.isEmpty())
+ return null;
+ Behavior[] behaviorNodes = new Behavior[behaviorVector.size()];
+ behaviorVector.copyInto(behaviorNodes);
+
+ return behaviorNodes;
+ }
+
+
+ /**
+ * This method returns an array of all of the Sound nodes defined
+ * in the file.
+ */
+ public Sound[] getSoundNodes() {
+ if (soundVector.isEmpty())
+ return null;
+ Sound[] soundNodes = new Sound[soundVector.size()];
+ soundVector.copyInto(soundNodes);
+ return soundNodes;
+ }
+
+
+ /**
+ * This method returns the text description of the file. If no
+ * such description exists, this method should return null.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+}
+
+
+
+
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/DebugOutput.java b/src/classes/share/com/sun/j3d/loaders/lw3d/DebugOutput.java
new file mode 100644
index 0000000..544b46b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/DebugOutput.java
@@ -0,0 +1,85 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+/**
+ * This class has utility functions for printing out debugging information.
+ */
+
+class DebugOutput {
+
+ int validOutput = 0;
+ final static int TRACE = 1, VALUES = 2, MISC = 4, LINE_TRACE = 8;
+ final static int NONE = 0, EXCEPTION = 16, TIME = 32, WARNING = 64;
+
+ DebugOutput(int outputTypes) {
+ validOutput = outputTypes;
+ }
+
+ void setValidOutput(int outputTypes) {
+ validOutput = outputTypes;
+ }
+
+ int getValidOutput() {
+ return validOutput;
+ }
+
+ void print(int outputType, String theOutput) {
+ if ((outputType & validOutput) > 0) {
+ System.out.print(theOutput);
+ }
+ }
+
+ void println(int outputType, String theOutput) {
+ if ((outputType & validOutput) > 0)
+ System.out.println(theOutput);
+ }
+
+}
+
+
+
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/EnvelopeHandler.java b/src/classes/share/com/sun/j3d/loaders/lw3d/EnvelopeHandler.java
new file mode 100644
index 0000000..faad047
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/EnvelopeHandler.java
@@ -0,0 +1,133 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.io.StreamTokenizer;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import com.sun.j3d.loaders.ParsingErrorException;
+import java.lang.ClassNotFoundException;
+import java.lang.InstantiationException;
+import java.lang.IllegalAccessException;
+import java.lang.reflect.InvocationTargetException;
+
+
+/**
+ * This class is used in implementing Envelope objects (of which there
+ * is currently only one - LightIntensity).
+ * The class is called whenever the parser has encountered
+ * a token which could have an envelope description. If the
+ * token simply has a numeric value, this value is stored.
+ * If, instead, there is an envelope, then the class creates
+ * the envelope class and parses that information.
+ */
+
+class EnvelopeHandler extends TextfileParser {
+
+ float theValue = 0;
+ boolean hasValue = false;
+ boolean hasEnvelope = true;
+ LwsEnvelope theEnvelope = null;
+
+ /**
+ * Constructor: This constructor is used if there is no existing
+ * implementation for this type of envelope. The real constructor
+ * is called with the generic LwsEnvelope class name, which will
+ * allow s to parse and ignore the envelope data
+ */
+ EnvelopeHandler(StreamTokenizer st,
+ int totalFrames, float totalTime) {
+ this(st, totalFrames, totalTime,
+ "com.sun.j3d.utils.loaders.lw3d.LwsEnvelope");
+ }
+
+ /**
+ * Constructor: This constructor is called with the name of a class
+ * that can handle the encountered envelope. This is done so that this
+ * EnvelopeHandler class can generically call the given class to handle
+ * the envelope, whether it results in parsing/ignoring the data or
+ * in actually using the data
+ */
+ EnvelopeHandler(StreamTokenizer st,
+ int totalFrames,
+ float totalTime,
+ String envClassName) throws ParsingErrorException {
+ try {
+ theValue = (float)getNumber(st);
+ hasValue = true;
+ }
+ catch (NumberFormatException e) {
+ if (st.ttype == '(') {
+ st.pushBack();
+ // This code creates a new instance for the given class name
+ try {
+ Class envClass = Class.forName(envClassName);
+ Constructor constructors[] = envClass.getConstructors();
+ Constructor con = constructors[0];
+ Object args[] = new Object[3];
+ args[0] = (Object)st;
+ args[1] = (Object)(new Integer(totalFrames));
+ args[2] = (Object)(new Float(totalTime));
+ try {
+ theEnvelope = (LwsEnvelope)con.newInstance(args);
+ hasEnvelope = true;
+ }
+ catch (InstantiationException e3) {
+ // Ignore
+ }
+ catch (IllegalAccessException e3) {
+ // Ignore
+ }
+ catch (InvocationTargetException e3) {
+ // Ignore
+ }
+ }
+ catch (ClassNotFoundException e2) {
+ // Ignore
+ }
+ }
+ }
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/FloatValueInterpolator.java b/src/classes/share/com/sun/j3d/loaders/lw3d/FloatValueInterpolator.java
new file mode 100644
index 0000000..c270c0f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/FloatValueInterpolator.java
@@ -0,0 +1,171 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import javax.vecmath.*;
+import java.util.BitSet;
+import java.util.Enumeration;
+
+import javax.media.j3d.Alpha;
+import javax.media.j3d.Node;
+import javax.media.j3d.NodeReferenceTable;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Interpolator;
+import com.sun.j3d.internal.J3dUtilsI18N;
+
+/**
+ * This class acts as an interpolator between values specified in a
+ * floating point array, based on knot values (keyframes) specified in a
+ * knots array.
+ */
+abstract class FloatValueInterpolator extends Interpolator {
+
+ private float knots[];
+ private int knotsLength;
+ protected int currentKnotIndex;
+ protected float currentInterpolationRatio;
+ protected float values[];
+ protected float currentValue;
+
+ /**
+ * Constructs a new FloatValueInterpolator object.
+ * @param alpha the alpha object for this interpolator
+ * @param knots an array of knot values that specify a spline
+ */
+ FloatValueInterpolator(Alpha alpha, float k[], float v[]) {
+
+ super(alpha);
+
+ // Check that first knot = 0.0f
+ knotsLength = k.length;
+ if (k[0] < -0.0001 || k[0] > 0.0001) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("FloatValueInterpolator0"));
+ }
+
+ // Check that last knot = 1.0f
+ if ((k[knotsLength-1] - 1.0f) < -0.0001 ||
+ (k[knotsLength-1] - 1.0f) > 0.0001) {
+
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("FloatValueInterpolator1"));
+ }
+
+ // Check to see that knots are in ascending order and copy them
+ this.knots = new float[knotsLength];
+ for (int i = 0; i < knotsLength; i++) {
+ if ((i > 0) && (k[i] < k[i-1])) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("FloatValueInterpolator2"));
+ }
+ this.knots[i] = k[i];
+ }
+
+ // check to see that we have the same number of values as knots
+ if (knotsLength != v.length) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("FloatValueInterpolator3"));
+ }
+
+ // copy the values
+ this.values = new float[knotsLength];
+ for(int i = 0; i < knotsLength; i++) {
+ this.values[i] = v[i];
+ }
+
+ }
+
+ /**
+ * This method sets the value at the specified index for
+ * this interpolator.
+ * @param index the index to be changed
+ * @param position the new value at index
+ */
+ void setValue(int index, float value) {
+ this.values[index] = value;
+ }
+
+ /**
+ * This method retrieves the value at the specified index.
+ * @param index the index of the value requested
+ * @return the interpolator's value at the index
+ */
+ float getValue(int index) {
+ return this.values[index];
+ }
+
+ /**
+ * This method computes the bounding knot indices and interpolation value
+ * "currentValue" given the current value of alpha, the knots[] array and
+ * the array of values.
+ * If the index is 0 and there will be no interpolation, both the
+ * index variable and the interpolation variable are set to 0.
+ * Otherwise, currentKnotIndex is set to the lower index of the
+ * two bounding knot points and the currentInterpolationRatio
+ * variable is set to the ratio of the alpha value between these
+ * two bounding knot points.
+ */
+ protected void computePathInterpolation() {
+ float alphaValue = (this.getAlpha()).value();
+
+ for (int i = 0; i < knotsLength; i++) {
+ if ((i == 0 && alphaValue <= knots[i]) ||
+ (i > 0 && alphaValue >= knots[i-1] && alphaValue <= knots[i])) {
+
+ if (i==0) {
+ currentInterpolationRatio = 0f;
+ currentKnotIndex = 0;
+ currentValue = values[0];
+ }
+ else {
+ currentInterpolationRatio =
+ (alphaValue - knots[i-1])/(knots[i] - knots[i-1]);
+ currentKnotIndex = i - 1;
+ currentValue = values[i-1] +
+ currentInterpolationRatio * (values[i] - values[i-1]);
+ }
+ break;
+ }
+ }
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/ImageScaler.java b/src/classes/share/com/sun/j3d/loaders/lw3d/ImageScaler.java
new file mode 100644
index 0000000..d457f9c
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/ImageScaler.java
@@ -0,0 +1,148 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+
+/**
+ * This class resizes an image to be the nearest power of 2 wide and high.
+ * This facility now exists inside of the TextureLoader class, so
+ * ImageScaler should be eliminated at some point.
+ */
+
+class ImageScaler {
+
+ int origW, origH;
+ Image origImage;
+
+ ImageScaler(Image image, int w, int h) {
+ origImage = image;
+ origW = w;
+ origH = h;
+ }
+
+ ImageScaler(BufferedImage image) {
+ origImage = image;
+ origW = image.getWidth();
+ origH = image.getHeight();
+ }
+
+ /**
+ * Utility method to return closes poser of 2 to the given integer
+ */
+ int getClosestPowerOf2(int value) {
+
+ if (value < 1)
+ return value;
+
+ int powerValue = 1;
+ for (int i = 1; i < 20; ++i) {
+ powerValue *= 2;
+ if (value < powerValue) {
+ // Found max bound of power, determine which is closest
+ int minBound = powerValue/2;
+ if ((powerValue - value) >
+ (value - minBound))
+ return minBound;
+ else
+ return powerValue;
+ }
+ }
+ // shouldn't reach here...
+ return 1;
+ }
+
+ /**
+ * Returns an Image that has been scaled from the original image to
+ * the closest power of 2
+ */
+ Image getScaledImage() {
+ int newWidth = getClosestPowerOf2(origW);
+ int newHeight = getClosestPowerOf2(origH);
+
+ // If the image is already a power of 2 wide/tall, no need to scale
+ if (newWidth == origW &&
+ newHeight == origH)
+ return origImage;
+
+ Image scaledImage = null;
+
+ if (origImage instanceof BufferedImage) {
+ // If BufferedImage, then we have some work to do
+ BufferedImage origImageB = (BufferedImage)origImage;
+ scaledImage =
+ new BufferedImage(newWidth,
+ newHeight,
+ origImageB.getType());
+ BufferedImage scaledImageB = (BufferedImage)scaledImage;
+ float widthScale = (float)origW/(float)newWidth;
+ float heightScale = (float)origH/(float)newHeight;
+ int origPixels[] = ((DataBufferInt)origImageB.getRaster().getDataBuffer()).getData();
+ int newPixels[] = ((DataBufferInt)scaledImageB.getRaster().getDataBuffer()).getData();
+ for (int row = 0; row < newHeight; ++row) {
+ for (int column = 0; column < newWidth; ++column) {
+ int oldRow = Math.min(origH-1,
+ (int)((float)(row)*
+ heightScale + .5f));
+ int oldColumn =
+ Math.min(origW-1,
+ (int)((float)column*widthScale + .5f));
+ newPixels[row*newWidth + column] =
+ origPixels[oldRow*origW + oldColumn];
+ }
+ }
+ }
+ else {
+ // If regular Image, then the work is done for us
+ scaledImage = origImage.getScaledInstance(newWidth,
+ newHeight,
+ Image.SCALE_DEFAULT);
+ }
+ return scaledImage;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/J3dLwoParser.java b/src/classes/share/com/sun/j3d/loaders/lw3d/J3dLwoParser.java
new file mode 100644
index 0000000..1db2dbb
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/J3dLwoParser.java
@@ -0,0 +1,601 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.awt.Component;
+import java.awt.Image;
+import java.util.Enumeration;
+import java.util.Vector;
+import com.sun.j3d.utils.geometry.GeometryInfo;
+import com.sun.j3d.utils.geometry.NormalGenerator;
+import com.sun.j3d.utils.geometry.Stripifier;
+import com.sun.j3d.utils.image.TextureLoader;
+import com.sun.j3d.loaders.IncorrectFormatException;
+import java.io.FileNotFoundException;
+
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+import java.net.*;
+
+
+/**
+ * This class is responsible for turning Lightwave geometry data into
+ * Java3D geometry. It is a subclass of LwoObject and calls that
+ * superclass when first instantiated to parse the binary file and turn it
+ * into intermediate data structures. J3dLwoParser then goes through
+ * those data structures and turns them into Java3D objects. For each
+ * ShapeHolder object created by the parent class, this class retrieves
+ * the geometry data and associated surface data, creates the
+ * appropriate Geometry object (usually IndexedTriangleFanArray,
+ * unless the object is points or lines), including calculating normals for
+ * the polygons and sets up the Appearance according to the surface
+ * parameters (including calculating texture coordinates if necessary).
+ */
+
+class J3dLwoParser extends LwoParser {
+
+ float normalCoordsArray[];
+ int normalIndicesArray[];
+ Shape3D objectShape;
+ Color3f color, diffuseColor, specularColor, emissiveColor;
+ float shininess;
+ Vector objectShapeList = new Vector();
+
+ /**
+ * Constructor: Calls LwoObject to parse file and create data structures
+ */
+ J3dLwoParser(String fileName,
+ int debugVals) throws FileNotFoundException {
+ super(fileName, debugVals);
+ }
+
+ J3dLwoParser(URL url, int debugVals)
+ throws FileNotFoundException {
+ super(url, debugVals);
+ }
+
+ void getSurf(int length) throws FileNotFoundException {
+ super.getSurf(length);
+ }
+
+
+ /**
+ * Turns LwoObject's data structures (created from the binary geometry
+ * file) into Java3d objects
+ */
+ void createJava3dGeometry() throws IncorrectFormatException {
+
+ GeometryArray object;
+ LwoTexture texture;
+
+ for (Enumeration e = shapeList.elements();
+ e.hasMoreElements() ;) {
+ int vertexFormat = javax.media.j3d.GeometryArray.COORDINATES;
+ ShapeHolder shape = (ShapeHolder)e.nextElement();
+ debugOutputLn(LINE_TRACE, "about to create Arrays for Shape");
+ debugOutputLn(VALUES, "shape = " + shape);
+ shape.createArrays(true);
+ int vertexCount = shape.coordsArray.length/3;
+ int indexCount = 0;
+ if (shape.facetIndices != null)
+ indexCount = shape.facetIndices.length;
+ debugOutputLn(VALUES, "numSurf = " + shape.numSurf);
+ // Find the right surface. Note: surfaces are indexed by
+ // name. So take this surf number, look up the name of that
+ // surface in surfaceNameList, then take that name and
+ // find the matching LwoSurface
+ String surfName =
+ (String)surfNameList.elementAt(shape.numSurf - 1);
+ LwoSurface surf = null;
+ for (int surfNum = 0;
+ surfNum < surfaceList.size();
+ ++surfNum) {
+ LwoSurface tempSurf =
+ (LwoSurface)surfaceList.elementAt(surfNum);
+ String tempSurfName = tempSurf.surfName;
+ if (surfName.equals(tempSurfName)) {
+ surf = tempSurf;
+ break;
+ }
+ }
+ if (surf == null) {
+ throw new IncorrectFormatException(
+ "bad surf for surfnum/name = " + shape.numSurf + ", " +
+ surfName);
+ }
+ debugOutputLn(VALUES, "surf = " + surf);
+
+ // Get the LwoTexture object (if any) for the surface
+ texture = surf.getTexture();
+
+ Appearance appearance = new Appearance();
+ if (shape.facetSizes[0] == 1) {
+ // This case happens if the objects are points
+ // Note that points are colored, not lit
+ object = new
+ javax.media.j3d.PointArray(vertexCount, vertexFormat);
+ object.setCoordinates(0, shape.coordsArray);
+ ColoringAttributes colorAtt =
+ new ColoringAttributes(surf.getColor(),
+ ColoringAttributes.FASTEST);
+ PointAttributes pointStyle = new PointAttributes();
+ pointStyle.setPointSize(1);
+
+ appearance.setColoringAttributes(colorAtt);
+ appearance.setPointAttributes(pointStyle);
+ }
+ else if (shape.facetSizes[0] == 2) {
+ // This case happens if the objects are lines
+ // Note that lines are colored, not lit
+ debugOutputLn(LINE_TRACE, "Creating IndexedLineArray");
+ object = new javax.media.j3d.LineArray(vertexCount,
+ vertexFormat);
+ object.setCoordinates(0, shape.coordsArray);
+ ColoringAttributes colorAtt =
+ new ColoringAttributes(surf.getColor(),
+ ColoringAttributes.FASTEST);
+ appearance.setColoringAttributes(colorAtt);
+ }
+ else {
+ // This is the case for any polygonal objects
+ debugOutputLn(LINE_TRACE, "Creating IndexedTriFanArray");
+ // create triFanArray
+ vertexFormat |= javax.media.j3d.GeometryArray.NORMALS;
+
+ debugOutputLn(LINE_TRACE, "about to process vertices/indices, facetIndices = " +
+ shape.facetIndices);
+ if (shape.facetIndices != null) {
+ float[] textureCoords = null;
+ int[] textureIndices = null;
+
+ debugOutputLn(LINE_TRACE, "setting vertexCount, normind = " + shape.normalIndices);
+ // If these are null we're going direct (non-indexed)
+ debugOutputLn(LINE_TRACE, "vtxcount, format, indcount = " +
+ vertexCount + ", " + vertexFormat +
+ ", " + indexCount);
+ if (texture != null) {
+ // There's a texture here - need to create the appropriate arrays
+ // and calculate texture coordinates for the object
+ vertexFormat |= GeometryArray.TEXTURE_COORDINATE_2;
+ textureCoords = new float[vertexCount * 2];
+ textureIndices = new int[shape.facetIndices.length];
+ calculateTextureCoords(texture, shape.coordsArray,
+ shape.facetIndices,
+ textureCoords, textureIndices);
+ debugOutputLn(LINE_TRACE, "textureCoords:");
+ debugOutputLn(LINE_TRACE, "texture Coords, Indices.length = " + textureCoords.length + ", " + textureIndices.length);
+ }
+ debugOutputLn(LINE_TRACE, "about to create GeometryInfo");
+
+ // Use the GeometryInfo utility to calculate smooth normals
+ GeometryInfo gi =
+ new GeometryInfo(GeometryInfo.TRIANGLE_FAN_ARRAY);
+ gi.setCoordinates(shape.coordsArray);
+ gi.setCoordinateIndices(shape.facetIndices);
+ gi.setStripCounts(shape.facetSizes);
+ if (texture != null) {
+ gi.setTextureCoordinateParams(1, 2);
+ gi.setTextureCoordinates(0, textureCoords);
+ gi.setTextureCoordinateIndices(0, textureIndices);
+ }
+ gi.recomputeIndices();
+ NormalGenerator ng =
+ new NormalGenerator(surf.getCreaseAngle());
+ ng.generateNormals(gi);
+ Stripifier st = new Stripifier();
+ st.stripify(gi);
+ object = gi.getGeometryArray(true, true, false);
+ debugOutputLn(LINE_TRACE, "done.");
+ }
+ else {
+ // This case is called if LwoObject did not create facet
+ // indices. This code is not currently used because facet
+ // indices are always created for polygonal objects
+ debugOutputLn(LINE_TRACE,
+ "about to create trifanarray with " +
+ "vertexCount, facetSizes.len = " +
+ vertexCount + ", " +
+ shape.facetSizes.length);
+ object = new
+ javax.media.j3d.TriangleFanArray(vertexCount,
+ vertexFormat,
+ shape.facetSizes);
+ object.setCoordinates(0, shape.coordsArray);
+ object.setNormals(0, shape.normalCoords);
+ debugOutputLn(VALUES, "passed in normalCoords, length = " +
+ shape.normalCoords.length);
+ }
+ debugOutputLn(LINE_TRACE, "created fan array");
+
+ // Setup Appearance given the surface parameters
+ Material material = new Material(surf.getColor(),
+ surf.getEmissiveColor(),
+ surf.getDiffuseColor(),
+ surf.getSpecularColor(),
+ surf.getShininess());
+ material.setLightingEnable(true);
+ appearance.setMaterial(material);
+ if (surf.getTransparency() != 0f) {
+ TransparencyAttributes ta = new TransparencyAttributes();
+ ta.setTransparency(surf.getTransparency());
+ ta.setTransparencyMode(ta.BLENDED);
+ appearance.setTransparencyAttributes(ta);
+ }
+ if (texture != null) {
+ debugOutputLn(LINE_TRACE, "texture != null, enable texturing");
+ Texture tex = texture.getTexture();
+ tex.setEnable(true);
+ appearance.setTexture(tex);
+ TextureAttributes ta = new TextureAttributes();
+ if (texture.getType().equals("DTEX"))
+ ta.setTextureMode(TextureAttributes.MODULATE);
+ else if (texture.getType().equals("CTEX"))
+ ta.setTextureMode(TextureAttributes.DECAL);
+ appearance.setTextureAttributes(ta);
+ }
+ else {
+ debugOutputLn(LINE_TRACE, "texture == null, no texture to use");
+ }
+ }
+ debugOutputLn(LINE_TRACE, "done creating object");
+
+ // This does gc
+ shape.nullify();
+
+ objectShape = new Shape3D(object);
+
+ // Combine the appearance and geometry
+ objectShape.setAppearance(appearance);
+ objectShapeList.addElement(objectShape);
+ }
+ }
+
+ /**
+ * Calculate texture coordinates for the geometry given the texture
+ * map properties specified in the LwoTexture object
+ */
+ void calculateTextureCoords(LwoTexture texture,
+ float verts[], int indices[],
+ float[] textureCoords, int[] textureIndices) {
+
+ /*
+ the actual math in these coord calculations comes directly from
+ Newtek - they posted sample code to help compute tex coords based
+ on the type of mapping:
+
+ Here are some simplified code fragments showing how LightWave
+ computes UV coordinates from X, Y, and Z. If the resulting
+ UV coordinates are not in the range from 0 to 1, the
+ appropriate integer should be added to them to bring them into
+ that range (the fract function should have accomplished
+ this by subtracting the floor of each number from itself).
+ Then they can be multiplied by the width and height (in pixels)
+ of an image map to determine which pixel to look up. The
+ texture size, center, and tiling parameters are taken right
+ off the texture control panel.
+
+
+ x -= xTextureCenter;
+ y -= yTextureCenter;
+ z -= zTextureCenter;
+ if (textureType == TT_PLANAR) {
+ s = (textureAxis == TA_X) ? z / zTextureSize + .5 :
+ x / xTextureSize + .5;
+ t = (textureAxis == TA_Y) ? -z / zTextureSize + .5 :
+ -y / yTextureSize + .5;
+ u = fract(s);
+ v = fract(t);
+ }
+ else if (type == TT_CYLINDRICAL) {
+ if (textureAxis == TA_X) {
+ xyztoh(z,x,-y,&lon);
+ t = -x / xTextureSize + .5;
+ }
+ else if (textureAxis == TA_Y) {
+ xyztoh(-x,y,z,&lon);
+ t = -y / yTextureSize + .5;
+ }
+ else {
+ xyztoh(-x,z,-y,&lon);
+ t = -z / zTextureSize + .5;
+ }
+ lon = 1.0 - lon / TWOPI;
+ if (widthTiling != 1.0)
+ lon = fract(lon) * widthTiling;
+ u = fract(lon);
+ v = fract(t);
+ }
+ else if (type == TT_SPHERICAL) {
+ if (textureAxis == TA_X)
+ xyztohp(z,x,-y,&lon,&lat);
+ else if (textureAxis == TA_Y)
+ xyztohp(-x,y,z,&lon,&lat);
+ else
+ xyztohp(-x,z,-y,&lon,&lat);
+ lon = 1.0 - lon / TWOPI;
+ lat = .5 - lat / PI;
+ if (widthTiling != 1.0)
+ lon = fract(lon) * widthTiling;
+ if (heightTiling != 1.0)
+ lat = fract(lat) * heightTiling;
+ u = fract(lon);
+ v = fract(lat);
+ }
+
+ support functions:
+
+ void xyztoh(float x,float y,float z,float *h)
+ {
+ if (x == 0.0 && z == 0.0)
+ *h = 0.0;
+ else {
+ if (z == 0.0)
+ *h = (x < 0.0) ? HALFPI : -HALFPI;
+ else if (z < 0.0)
+ *h = -atan(x / z) + PI;
+ else
+ *h = -atan(x / z);
+ }
+ }
+
+ void xyztohp(float x,float y,float z,float *h,float *p)
+ {
+ if (x == 0.0 && z == 0.0) {
+ *h = 0.0;
+ if (y != 0.0)
+ *p = (y < 0.0) ? -HALFPI : HALFPI;
+ else
+ *p = 0.0;
+ }
+ else {
+ if (z == 0.0)
+ *h = (x < 0.0) ? HALFPI : -HALFPI;
+ else if (z < 0.0)
+ *h = -atan(x / z) + PI;
+ else
+ *h = -atan(x / z);
+ x = sqrt(x * x + z * z);
+ if (x == 0.0)
+ *p = (y < 0.0) ? -HALFPI : HALFPI;
+ else
+ *p = atan(y / x);
+ }
+ }
+ */
+
+ debugOutputLn(TRACE, "calculateTextureCoords()");
+ // Compute texture coord stuff
+ float sx = 0, sz = 0, ty = 0, tz = 0;
+ double s, t;
+ int textureAxis = texture.getTextureAxis();
+ Vector3f textureSize = texture.getTextureSize();
+ Vector3f textureCenter = texture.getTextureCenter();
+
+ String mappingType = texture.getMappingType();
+ if (mappingType.startsWith("Cylindrical"))
+ calculateCylindricalTextureCoords(textureAxis, textureSize,
+ textureCenter, textureCoords,
+ textureIndices,
+ verts, indices);
+ else if (mappingType.startsWith("Spherical"))
+ calculateSphericalTextureCoords(textureAxis,
+ textureCenter, textureCoords,
+ textureIndices,
+ verts, indices);
+ else if (mappingType.startsWith("Planar"))
+ calculatePlanarTextureCoords(textureAxis, textureSize,
+ textureCenter, textureCoords,
+ textureIndices,
+ verts, indices);
+
+ }
+
+ /** See the comments in calculateTextureCoordinates*/
+ double xyztoh(float x,float y,float z) {
+ if (x == 0.0 && z == 0.0)
+ return 0.0;
+ else {
+ if (z == 0.0)
+ return (x < 0.0) ? Math.PI/2.0 : -Math.PI/2.0;
+ else if (z < 0.0)
+ return -Math.atan(x / z) + Math.PI;
+ else
+ return -Math.atan(x / z);
+ }
+ }
+
+ /** See the comments in calculateTextureCoordinates*/
+ double xyztop(float x,float y,float z) {
+ double p;
+
+ if (x == 0.0 && z == 0.0) {
+ if (y != 0.0)
+ p = (y < 0.0) ? -Math.PI/2 : Math.PI/2;
+ else
+ p = 0.0;
+ }
+ else {
+ x = (float)Math.sqrt(x * x + z * z);
+ if (x == 0.0)
+ p = (y < 0.0) ? -Math.PI/2 : Math.PI/2;
+ else
+ p = Math.atan(y / x);
+ }
+ return p;
+ }
+
+
+ /** See the comments in calculateTextureCoordinates*/
+ void calculateSphericalTextureCoords(int textureAxis,
+ Vector3f textureCenter,
+ float textureCoords[],
+ int textureIndices[],
+ float verts[], int indices[]) {
+ debugOutputLn(TRACE, "calculateSphericalTextureCoords");
+ double s, t;
+
+
+ for (int i = 0; i < indices.length; ++i) {
+ float x = verts[3*indices[i]] - textureCenter.x;
+ float y = verts[3*indices[i]+1] - textureCenter.y;
+ float z = -(verts[3*indices[i]+2] + textureCenter.z);
+ if (textureAxis == 1){ // X Axis
+ s = xyztoh(z, x, -y);
+ t = xyztop(z,x,-y);
+ }
+ else if (textureAxis == 2) { // Y Axis
+ s = xyztoh(-x,y,z);
+ t = xyztop(-x,y,z);
+ }
+ else { // Z Axis
+ s = xyztoh(-x,z,-y);
+ t = xyztop(-x,z,-y);
+ }
+ s = 1.0 - s / (2*Math.PI);
+ t = -(.5 - t / Math.PI);
+ textureCoords[indices[i]*2] = (float)s;
+ textureCoords[indices[i]*2 + 1] = (float)t;
+ textureIndices[i] = indices[i];
+ }
+ }
+
+ /** See the comments in calculateTextureCoordinates*/
+ void calculateCylindricalTextureCoords(int textureAxis,
+ Vector3f textureSize,
+ Vector3f textureCenter,
+ float textureCoords[],
+ int textureIndices[],
+ float verts[], int indices[]) {
+ debugOutputLn(TRACE, "calculateCylindricalTextureCoords");
+ debugOutputLn(VALUES, "axis, size, center, tc, ti, v, i = " +
+ textureAxis + ", " +
+ textureSize + ", " +
+ textureCenter + ", " +
+ textureCoords + ", " +
+ textureIndices + ", " +
+ verts + ", " +
+ indices);
+ double s, t;
+
+ debugOutputLn(VALUES, "Cyl Texture Coords:");
+ for (int i = 0; i < indices.length; ++i) {
+ float x = verts[3*indices[i]] - textureCenter.x;
+ float y = verts[3*indices[i]+1] - textureCenter.y;
+ float z = -(verts[3*indices[i]+2] + textureCenter.z);
+ // Negate z value because we invert geom z's to swap handedness
+ if (textureAxis == 1) { // X axis
+ s = xyztoh(z,x,-y);
+ t = x / textureSize.x + .5;
+ }
+ else if (textureAxis == 2) { // Y Axis
+ s = xyztoh(-x,y,z);
+ t = y / textureSize.y + .5;
+ }
+ else {
+ s = xyztoh(-x,z,-y);
+ t = z / textureSize.z + .5;
+ }
+ s = 1.0 - s / (2*Math.PI);
+ textureCoords[indices[i]*2] = (float)s;
+ textureCoords[indices[i]*2 + 1] = (float)t;
+ textureIndices[i] = indices[i];
+ debugOutputLn(VALUES, "x, y, z = " +
+ x + ", " + y + ", " + z + " " +
+ "s, t = " + s + ", " + t);
+ }
+ }
+
+ /** See the comments in calculateTextureCoordinates*/
+ void calculatePlanarTextureCoords(int textureAxis, Vector3f textureSize,
+ Vector3f textureCenter,
+ float textureCoords[],
+ int textureIndices[],
+ float verts[], int indices[]) {
+ debugOutputLn(TRACE, "calculatePlanarTextureCoords");
+ debugOutputLn(VALUES, "size, center, axis = " +
+ textureSize + textureCenter + ", " + textureAxis);
+ float sx = 0, sz = 0, ty = 0, tz = 0;
+ double s, t;
+
+ if (textureAxis == 1) { // X Axis
+ sz = -1.0f / textureSize.z; // Negate because we negate z in geom
+ ty = 1.0f / textureSize.y;
+ }
+ else if (textureAxis == 2) { // Y Axis
+ sx = 1.0f / textureSize.x;
+ tz = -1.0f / textureSize.z; // Negate because we negate z in geom
+ }
+ else { // Z Axis
+ sx = 1.0f / textureSize.x;
+ ty = 1.0f / textureSize.y;
+ }
+
+ debugOutputLn(VALUES, "Planar Texture Coords:");
+ for (int i = 0; i < indices.length; ++i) {
+ float x = verts[3*indices[i]] - textureCenter.x;
+ float y = verts[3*indices[i]+1] - textureCenter.y;
+ float z = verts[3*indices[i]+2] + textureCenter.z;
+ s = x*sx + z*sz + .5;
+ t = y*ty + z*tz + .5;
+ textureCoords[indices[i]*2] = (float)s;
+ textureCoords[indices[i]*2 + 1] = (float)t;
+ textureIndices[i] = indices[i];
+ debugOutputLn(VALUES, "x, y, z = " +
+ x + ", " + y + ", " + z + " " +
+ "s, t = " + s + ", " + t);
+ }
+ }
+
+
+ Shape3D getJava3dShape() {
+ return objectShape;
+ }
+
+ Vector getJava3dShapeList() {
+ return objectShapeList;
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LWOBFileReader.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LWOBFileReader.java
new file mode 100644
index 0000000..d83bfe4
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LWOBFileReader.java
@@ -0,0 +1,266 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileInputStream;
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import com.sun.j3d.loaders.ParsingErrorException;
+
+
+class LWOBFileReader extends BufferedInputStream {
+
+
+
+ // Debugging constants
+ final static int TRACE = DebugOutput.TRACE;
+ final static int VALUES = DebugOutput.VALUES;
+ final static int MISC = DebugOutput.MISC;
+ final static int LINE_TRACE = DebugOutput.LINE_TRACE;
+ final static int NONE = DebugOutput.NONE;
+ final static int EXCEPTION = DebugOutput.EXCEPTION;
+
+ protected DebugOutput debugPrinter;
+
+ protected String theFilename;
+
+ protected int marker;
+
+
+
+ protected void debugOutputLn(int outputType, String theOutput) {
+ if (theOutput.equals(""))
+ debugPrinter.println(outputType, theOutput);
+ else
+ debugPrinter.println(outputType,
+ getClass().getName() + "::" + theOutput);
+ } // End of debugOutputLn
+
+
+
+ // Return a string consisting of the next 4 bytes in the file
+ public String getToken() throws ParsingErrorException {
+ byte tokenBuffer[] = new byte[4];
+ try {
+ int readResult = read(tokenBuffer, 0, 4);
+ if (readResult == -1) {
+ debugOutputLn(LINE_TRACE, "no token - returning null");
+ return null;
+ }
+ return new String(tokenBuffer);
+ }
+ catch (IOException e) {
+ debugOutputLn(EXCEPTION, "getToken: " + e);
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+
+
+
+ /**
+ * Skip ahead amount bytes in the file
+ */
+ public void skipLength(int amount) throws ParsingErrorException {
+ try {
+ skip((long)amount);
+ marker += amount;
+ }
+ catch (IOException e) {
+ debugOutputLn(EXCEPTION, "skipLength: " + e);
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+
+
+
+ /**
+ * Read four bytes from the file and return their integer value
+ */
+ public int getInt() throws ParsingErrorException {
+ try {
+ int x = 0;
+ for (int i = 0 ; i < 4 ; i++) {
+ int readResult = read();
+ if (readResult == -1)
+ throw new ParsingErrorException("Unexpected EOF");
+ x = (x << 8) | readResult;
+ }
+ return x;
+ }
+ catch (IOException e) {
+ debugOutputLn(EXCEPTION, "getInt: " + e);
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+
+
+
+ /**
+ * Read four bytes from the file and return their float value
+ */
+ public float getFloat() throws ParsingErrorException {
+ return Float.intBitsToFloat(getInt());
+ } // End of getFloat
+
+
+
+ /**
+ * Returns the name of the file associated with this stream
+ */
+ public String getFilename() {
+ return theFilename;
+ } // End of getFilename
+
+
+
+ /**
+ * Returns a string read from the file. The string is assumed to
+ * end with '0'.
+ */
+ public String getString() throws ParsingErrorException {
+ byte buf[] = new byte[512];
+ try {
+ byte b;
+ int len = 0;
+ do {
+ b = (byte)read();
+ buf[len++] = b;
+ } while (b != 0);
+ // Have to read an even number of bytes
+ if (len % 2 != 0) read();
+ }
+ catch (IOException e) {
+ debugOutputLn(EXCEPTION, "getString: " + e);
+ throw new ParsingErrorException(e.getMessage());
+ }
+ return new String(buf);
+ } // End of getString
+
+
+
+ /**
+ * Reads an array of xyz values.
+ */
+ public void getVerts(float ar[], int num) throws ParsingErrorException {
+ for (int i = 0 ; i < num ; i++) {
+ ar[i * 3 + 0] = getFloat();
+ ar[i * 3 + 1] = getFloat();
+ ar[i * 3 + 2] = -getFloat();
+ }
+ } // End of getVerts
+
+
+
+ /**
+ * Reads two bytes from the file and returns their integer value.
+ */
+ public int getShortInt() throws ParsingErrorException {
+ int i = 0;
+ try {
+ i = read();
+ i = (i << 8) | read();
+ // Sign extension
+ if ((i & 0x8000) != 0) i |= 0xffff0000;
+ }
+ catch (IOException e) {
+ debugOutputLn(EXCEPTION, "getShortInt: " + e);
+ throw new ParsingErrorException(e.getMessage());
+ }
+ return i;
+ } // End of getShortInt
+
+
+
+ /**
+ * Returns the current position in the file
+ */
+ public int getMarker() {
+ // protected field inherited from BufferedInputStream
+ return marker;
+ } // End of getMarker
+
+
+
+ public int read() throws IOException {
+ marker++;
+ return super.read();
+ } // End of read()
+
+
+
+ public int read(byte[] buffer, int offset, int count) throws IOException {
+ int ret = super.read(buffer, offset, count);
+ if (ret != -1) marker += ret;
+ return ret;
+ } // End of read(byte[], int, int)
+
+
+
+ /**
+ * Constructor.
+ */
+ public LWOBFileReader(String filename) throws FileNotFoundException {
+ super(new FileInputStream(filename));
+
+ // Add constants on this line to get more debug output
+ debugPrinter = new DebugOutput(127);
+
+ marker = 0;
+ } // End of constructor
+
+ public LWOBFileReader(java.net.URL url) throws java.io.IOException {
+ super(url.openStream());
+
+ // add constants on this line to get more debug output
+ debugPrinter = new DebugOutput(127);
+
+ marker = 0;
+ }
+
+} // End of file LWOBFileReader
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LightIntensityPathInterpolator.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LightIntensityPathInterpolator.java
new file mode 100644
index 0000000..61ba2fb
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LightIntensityPathInterpolator.java
@@ -0,0 +1,101 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.util.*;
+
+import javax.media.j3d.Alpha;
+import javax.media.j3d.Light;
+
+/**
+ * This Interpolator object modifies the intensity of a Light object
+ * according to the keyframes in a light intensity envelope
+ */
+class LightIntensityPathInterpolator extends FloatValueInterpolator {
+
+ LwLightObject theLight;
+
+ LightIntensityPathInterpolator(Alpha alpha,
+ float knots[],
+ float values[],
+ Object target) {
+
+ super(alpha, knots, values);
+ theLight = (LwLightObject)target;
+ }
+
+ /**
+ * This method is invoked by the behavior scheduler every frame. It maps
+ * the alpha value that corresponds to the current time into the
+ * appropriate light intensity for that time as obtained by interpolating
+ * between the light intensity values for each knot point that were passed
+ * to this class.
+ * @param criteria enumeration of criteria that have triggered this wakeup
+ */
+
+ public void processStimulus(Enumeration criteria) {
+ // Handle stimulus
+
+ if (this.getAlpha() != null) {
+
+ // Let FloatValueInterpolator calculate the correct
+ // interpolated value
+ computePathInterpolation();
+
+ // Set light intensity to the value calculated by
+ // FloatValueInterpolator
+ if (theLight != null)
+ theLight.setIntensity(currentValue);
+
+ if ((this.getAlpha()).finished())
+ return;
+ }
+
+ wakeupOn(defaultWakeupCriterion);
+
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/Lw3dLoader.java b/src/classes/share/com/sun/j3d/loaders/lw3d/Lw3dLoader.java
new file mode 100644
index 0000000..c783518
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/Lw3dLoader.java
@@ -0,0 +1,706 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+import com.sun.j3d.loaders.*;
+import java.awt.Component;
+import java.io.*;
+import java.util.Vector;
+import java.util.Enumeration;
+import java.net.URL;
+import java.net.MalformedURLException;
+
+import javax.media.j3d.*;
+import javax.vecmath.Color3f;
+import javax.vecmath.Point3d;
+
+
+/**
+ * This class implements the Loader API and allows users to load
+ * Lightwave 3D scene files. In order to load properly, the object
+ * files referred to in the scene files and the image files referred
+ * to by the object files must all be specified with path and filenames
+ * that are valid with respect to the directory in which the application
+ * is being executed.
+ */
+
+public class Lw3dLoader extends TextfileParser implements Loader {
+
+ Vector objectList;
+ Vector lightList;
+ BranchGroup sceneGroupNode;
+ Color3f ambientColor;
+ LwsCamera camera = null;
+ LwsFog fog = null;
+ LwsBackground background = null;
+ int loadFlags = 0;
+ int loadBehaviors = 0;
+ Vector sceneBehaviors;
+ SceneBase scene = null;
+ String basePath = null;
+ String internalBasePath = null;
+ URL baseUrl = null;
+ String internalBaseUrl = null; // store url base as String
+ static final int FILE_TYPE_NONE = 0;
+ static final int FILE_TYPE_URL = 1;
+ static final int FILE_TYPE_FILENAME = 2;
+ static final int FILE_TYPE_READER = 4;
+ int fileType = FILE_TYPE_NONE;
+
+ /**
+ * Default constructor. Sets up default values for some variables.
+ */
+ public Lw3dLoader() {
+
+ ambientColor = new Color3f(0f, 0f, 0f);
+ objectList = new Vector();
+ lightList = new Vector();
+ debugPrinter.setValidOutput(0x0);
+
+ }
+
+ /**
+ * This constructor takes a flags word that specifies which types of
+ * scenefile items should be loaded into the scene. The possible
+ * values are specified in the com.sun.j3d.loaders.Loader class.
+ */
+ public Lw3dLoader(int flags) {
+
+ this();
+ loadFlags = flags;
+ loadBehaviors = (loadFlags & Loader.LOAD_BEHAVIOR_NODES);
+
+ }
+
+ /**
+ * This method loads the named file and returns the Scene
+ * containing the scene. Any data files referenced by the Reader
+ * should be located in the same place as the named file; otherwise,
+ * users should specify an alternate base path with the setBaseUrl(URL)
+ * method.
+ */
+ public Scene load(URL url) throws FileNotFoundException,
+ IncorrectFormatException, ParsingErrorException {
+
+ fileType = FILE_TYPE_URL;
+ setInternalBaseUrl(url);
+ InputStreamReader reader;
+ try {
+ reader = new InputStreamReader(
+ new BufferedInputStream(url.openStream()));
+ }
+ catch (IOException e) {
+ throw new FileNotFoundException(e.getMessage());
+ }
+ Scene returnScene = load(reader);
+ fileType = FILE_TYPE_NONE;
+ return returnScene;
+ }
+
+ /**
+ * This method loads the named file and returns the Scene
+ * containing the scene. Any data files referenced by this
+ * file should be located in the same place as the named file;
+ * otherwise users should specify an alternate base path with
+ * the setBasePath(String) method.
+ */
+ public Scene load(String fileName) throws FileNotFoundException,
+ IncorrectFormatException, ParsingErrorException {
+
+ fileType = FILE_TYPE_FILENAME;
+ setInternalBasePath(fileName);
+ Reader reader = new BufferedReader(new FileReader(fileName));
+ Scene returnScene = load(reader);
+ fileType = FILE_TYPE_NONE;
+ return returnScene;
+ }
+
+ /**
+ * This method loads the Reader and returns the Scene
+ * containing the scene. Any data files referenced by the Reader should
+ * be located in the user's current working directory.
+ */
+ public Scene load(Reader reader) throws FileNotFoundException,
+ IncorrectFormatException, ParsingErrorException {
+
+ if (fileType == FILE_TYPE_NONE)
+ fileType = FILE_TYPE_READER;
+ StreamTokenizer tokenizer = new StreamTokenizer(reader);
+ setupTokenizer(tokenizer);
+
+ getAndCheckString(tokenizer, "LWSC");
+ getNumber(tokenizer);
+ getAndCheckString(tokenizer, "FirstFrame");
+ int firstFrame = (int)getNumber(tokenizer);
+ getAndCheckString(tokenizer, "LastFrame");
+ int finalFrame = (int)getNumber(tokenizer);
+ skipUntilString(tokenizer, "FramesPerSecond");
+ double fps = getNumber(tokenizer);
+ float totalTime = (float)(finalFrame - firstFrame)/(float)fps;
+ boolean done = false;
+ while (!done) {
+ int token;
+ try {
+ token = tokenizer.nextToken();
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ switch (tokenizer.ttype) {
+ case StreamTokenizer.TT_EOF:
+ done = true;
+ break;
+ case StreamTokenizer.TT_WORD:
+ debugOutputLn(VALUES, " String = " + tokenizer.sval);
+ if (tokenizer.sval.equals("AddNullObject")) {
+ LwsObject obj =
+ new LwsObject(tokenizer, false,
+ firstFrame,
+ finalFrame, totalTime,
+ this,
+ debugPrinter.getValidOutput());
+ obj.createJava3dObject(null, loadBehaviors);
+ objectList.addElement(obj);
+ }
+ else if (tokenizer.sval.equals("LoadObject")) {
+ String filename = getString(tokenizer);
+ tokenizer.pushBack(); // push filename token back
+ debugOutputLn(TIME, "loading " + filename + " at " +
+ System.currentTimeMillis());
+ LwsObject obj = new LwsObject(tokenizer, true,
+ firstFrame,
+ finalFrame, totalTime,
+ this,
+ debugPrinter.getValidOutput());
+ debugOutputLn(TIME, "done loading at " +
+ System.currentTimeMillis());
+ LwsObject cloneObject = null;
+ for (Enumeration e = objectList.elements() ;
+ e.hasMoreElements() ;) {
+ LwsObject tmpObj = (LwsObject)e.nextElement();
+ if (tmpObj.fileName != null &&
+ tmpObj.fileName.equals(filename)) {
+ cloneObject = tmpObj;
+ break;
+ }
+ }
+ obj.createJava3dObject(cloneObject, loadBehaviors);
+ objectList.addElement(obj);
+ }
+ else if (tokenizer.sval.equals("AmbientColor")) {
+ ambientColor.x = (float)getNumber(tokenizer)/255f;
+ ambientColor.y = (float)getNumber(tokenizer)/255f;
+ ambientColor.z = (float)getNumber(tokenizer)/255f;
+ }
+ else if (tokenizer.sval.equals("AmbIntensity")) {
+ // TODO: must be able to handle envelopes here
+ float intensity = (float)getNumber(tokenizer);
+ ambientColor.x *= intensity;
+ ambientColor.y *= intensity;
+ ambientColor.z *= intensity;
+ }
+ else if (tokenizer.sval.equals("AddLight")) {
+ LwsLight light =
+ new LwsLight(tokenizer,
+ finalFrame, totalTime,
+ debugPrinter.getValidOutput());
+ light.createJava3dObject(loadBehaviors);
+ lightList.addElement(light);
+ }
+ else if (tokenizer.sval.equals("ShowCamera")) {
+ camera = new LwsCamera(tokenizer, firstFrame,
+ finalFrame, totalTime,
+ debugPrinter.getValidOutput());
+ camera.createJava3dObject(loadBehaviors);
+ }
+ else if (tokenizer.sval.equals("FogType")) {
+ int fogType = (int)getNumber(tokenizer);
+ if (fogType != 0) {
+ fog = new LwsFog(tokenizer,
+ debugPrinter.getValidOutput());
+ fog.createJava3dObject();
+ }
+ }
+ else if (tokenizer.sval.equals("SolidBackdrop")) {
+ background =
+ new LwsBackground(tokenizer,
+ debugPrinter.getValidOutput());
+ background.createJava3dObject();
+ }
+ break;
+ default:
+ debugOutputLn(VALUES, " Unknown ttype, token = " +
+ tokenizer.ttype + ", " + token);
+ break;
+ }
+ }
+
+ // Set up scene groups and parent objects appropriately
+ sceneGroupNode = new BranchGroup();
+ sceneBehaviors = new Vector();
+ parentObjects();
+ constructScene();
+
+ return scene;
+
+ }
+
+
+ /**
+ * This method creates the Scene (actually SceneBase) data structure
+ * and adds all appropriate items to it. This is the data structure
+ * that the user will get back from the load() call and inquire to
+ * get data from the scene.
+ */
+ void constructScene() {
+
+ // Construct Scene data structure
+ scene = new SceneBase();
+
+ if ((loadFlags & Loader.LOAD_LIGHT_NODES) != 0) {
+ addLights();
+ addAmbient();
+ }
+
+ if ((loadFlags & Loader.LOAD_FOG_NODES) != 0)
+ addFog();
+
+ if ((loadFlags & Loader.LOAD_BACKGROUND_NODES) != 0)
+ addBackground();
+
+ if ((loadFlags & Loader.LOAD_VIEW_GROUPS) != 0)
+ addCamera();
+
+ if (loadBehaviors != 0)
+ addBehaviors();
+
+ scene.setSceneGroup(sceneGroupNode);
+
+ // now add named objects to the scenes name table
+ for (Enumeration e = objectList.elements(); e.hasMoreElements() ;) {
+
+ LwsObject obj = (LwsObject)e.nextElement();
+ if (obj.fileName != null)
+ scene.addNamedObject(obj.fileName,(Object)obj.getObjectNode());
+ else if (obj.objName != null)
+ scene.addNamedObject(obj.objName,(Object)obj.getObjectNode());
+
+ }
+ }
+
+
+ /**
+ * Creates a url for internal use. This method is not currently
+ * used (url's are ignored for this loader; only filenames work)
+ */
+ void setInternalBaseUrl(URL url) {
+// System.out.println("setInternalBaseUrl url = " + url);
+ java.util.StringTokenizer stok =
+ new java.util.StringTokenizer(url.toString(),
+// java.io.File.separator);
+ "\\/");
+ int tocount = stok.countTokens()-1;
+ StringBuffer sb = new StringBuffer(80);
+ for(int ji = 0; ji < tocount ; ji++) {
+ String a = stok.nextToken();
+ if((ji == 0) && //(!a.equals("file:"))) {
+ (!a.regionMatches(true, 0, "file:", 0, 5))) {
+ sb.append(a);
+ // urls use / on windows also
+// sb.append(java.io.File.separator);
+// sb.append(java.io.File.separator);
+ sb.append('/');
+ sb.append('/');
+ } else {
+ sb.append(a);
+ // urls use / on windows also
+// sb.append( java.io.File.separator );
+ sb.append('/');
+ }
+ }
+ internalBaseUrl = sb.toString();
+// System.out.println("internalBaseUrl = " + internalBaseUrl);
+ }
+
+ /**
+ * Standardizes the filename for use in the loader
+ */
+ void setInternalBasePath(String fileName) {
+ java.util.StringTokenizer stok =
+ new java.util.StringTokenizer(fileName,
+ java.io.File.separator);
+ int tocount = stok.countTokens()-1;
+ StringBuffer sb = new StringBuffer(80);
+ if (fileName!= null &&
+ fileName.startsWith(java.io.File.separator))
+ sb.append(java.io.File.separator);
+ for(int ji = 0; ji < tocount ; ji++) {
+ String a = stok.nextToken();
+ sb.append(a);
+ sb.append( java.io.File.separator );
+ }
+ internalBasePath = sb.toString();
+ }
+
+ String getInternalBasePath() {
+ return internalBasePath;
+ }
+
+ String getInternalBaseUrl() {
+ return internalBaseUrl;
+ }
+
+ int getFileType() {
+ return fileType;
+ }
+
+ /**
+ * This method parents all objects in the scene appropriately. If
+ * the scen file specifies a Parent node for the object, then the
+ * object is parented to that node. If not, then the object is
+ * parented to the scene's root.
+ */
+ void parentObjects() {
+ debugOutputLn(TRACE, "parentObjects()");
+ for (Enumeration e = objectList.elements(); e.hasMoreElements(); ) {
+
+ LwsObject obj = (LwsObject)e.nextElement();
+ if (obj.getParent() != -1) {
+
+ LwsObject parent = (LwsObject)
+ objectList.elementAt(obj.getParent() - 1);
+ parent.addChild(obj);
+ debugOutputLn(VALUES, "added child successfully");
+
+ } else {
+
+ if (obj.getObjectNode() != null)
+ sceneGroupNode.addChild(obj.getObjectNode());
+
+ }
+
+ // Collect all behaviors
+ if (loadBehaviors != 0) {
+ if (!(obj.getObjectBehaviors()).isEmpty()) {
+ sceneBehaviors.addAll(obj.getObjectBehaviors());
+
+ }
+ }
+ }
+
+ debugOutputLn(LINE_TRACE, "Done with parentObjects()");
+
+ }
+
+
+ /**
+ * This method sets the base URL name for data files
+ * associated with the file passed into the load(URL) method.
+ * The basePath should be null by default, which is an
+ * indicator to the loader that it should look for any
+ * associated files starting from the same directory as the
+ * file passed into the load(URL) method.
+ */
+ public void setBaseUrl(URL url) {
+ baseUrl = url;
+ }
+
+ /**
+ * This method sets the base path to be used when searching for all
+ * data files within a Lightwave scene.
+ */
+ public void setBasePath(String pathName) {
+ // This routine standardizes path names so that all pathnames
+ // will have standard file separators, they'll end in a separator
+ // character, and if the user passes in null or "" (meaning to
+ // set the current directory as the base path), this will become
+ // "./" (or ".\")
+ basePath = pathName;
+ if (basePath == null || basePath == "")
+ basePath = "." + java.io.File.separator;
+ basePath = basePath.replace('/', java.io.File.separatorChar);
+ basePath = basePath.replace('\\', java.io.File.separatorChar);
+ if (!basePath.endsWith(java.io.File.separator))
+ basePath = basePath + java.io.File.separator;
+ }
+
+ /**
+ * Returns the current base URL setting.
+ */
+ public URL getBaseUrl() {
+ return baseUrl;
+ }
+
+ /**
+ * Returns the current base path setting.
+ */
+ public String getBasePath() {
+ return basePath;
+ }
+
+ /**
+ * This method sets the load flags for the file. The flags should
+ * equal 0 by default (which tells the loader to only load geometry).
+ */
+ public void setFlags(int flags) {
+ loadFlags = flags;
+ }
+
+ /**
+ * Returns the current loading flags setting.
+ */
+ public int getFlags() {
+ return loadFlags;
+ }
+
+
+
+ /**
+ * getObject() iterates through the objectList checking the given
+ * name against the fileName and objectName of each object in turn.
+ * For the filename, it carves off the pathname and just checks the
+ * final name (e.g., "missile.lwo").
+ * If name has []'s at the end, it will use the number inside those
+ * brackets to pick which object out of an ordered set it will
+ * send back (objectList is created in the order that objects
+ * exist in the file, so this order should correspond to the order
+ * specified by the user). If no []'s exist, just pass back the
+ * first one encountered that matches.
+ */
+ public TransformGroup getObject(String name) {
+ debugOutputLn(TRACE, "getObject()");
+ int indexNumber = -1;
+ int currentObjectCount = 0;
+ String subobjectName = name;
+ if (name.indexOf("[") != -1) {
+ // caller wants specifically numbered subjbect; get that number
+ int bracketsIndex = name.indexOf("[");
+ subobjectName = name.substring(0, bracketsIndex);
+ String bracketsString = name.substring(bracketsIndex);
+ int bracketEndIndex = bracketsString.indexOf("]");
+ String indexString = bracketsString.substring(1, bracketEndIndex);
+ indexNumber = (new Integer(indexString)).intValue();
+ }
+ for (Enumeration e = objectList.elements() ;
+ e.hasMoreElements() ;) {
+ LwsObject tempObj = (LwsObject)e.nextElement();
+ debugOutputLn(VALUES, "tempObj, file, objname = " +
+ tempObj + tempObj.fileName +
+ tempObj.objName);
+ if ((tempObj.fileName != null &&
+ tempObj.fileName.indexOf(subobjectName) != -1) ||
+ (tempObj.objName != null &&
+ tempObj.objName.indexOf(subobjectName) != -1)) {
+ if (indexNumber < 0 ||
+ indexNumber == currentObjectCount)
+ return tempObj.getObjectNode();
+ else
+ currentObjectCount++;
+ }
+ }
+ debugOutputLn(VALUES, " no luck - wanted " +
+ name + " returning null");
+ return null;
+ }
+
+
+ /**
+ * This method sets up the StreamTokenizer for the scene file. Note
+ * that we're not parsing numbers as numbers because the tokenizer
+ * does not interpret scientific notation correctly.
+ */
+ void setupTokenizer(StreamTokenizer tokenizer) {
+ tokenizer.resetSyntax();
+ tokenizer.wordChars('a', 'z');
+ tokenizer.wordChars('A', 'Z');
+ tokenizer.wordChars(128 + 32, 255);
+ tokenizer.whitespaceChars(0, ' ');
+ tokenizer.commentChar('/');
+ tokenizer.quoteChar('"');
+ tokenizer.quoteChar('\'');
+ tokenizer.wordChars('0', '9');
+ tokenizer.wordChars('.', '.');
+ tokenizer.wordChars('-', '-');
+ tokenizer.wordChars('/', '/');
+ tokenizer.wordChars('\\', '\\');
+ tokenizer.wordChars('_', '_');
+ tokenizer.wordChars('&', '&');
+ tokenizer.ordinaryChar('(');
+ tokenizer.ordinaryChar(')');
+ tokenizer.whitespaceChars('\r', '\r');
+
+ // add ':' as wordchar so urls will work
+ tokenizer.wordChars(':', ':');
+ // add '~' as wordchar for urls
+ tokenizer.wordChars('~', '~');
+ }
+
+ /**
+ * Adds Ambient lighting effects to the scene
+ */
+ void addAmbient() {
+ AmbientLight aLgt = new AmbientLight(ambientColor);
+ BoundingSphere bounds =
+ new BoundingSphere(new Point3d(0.0,0.0,0.0), 100000.0);
+ aLgt.setInfluencingBounds(bounds);
+ sceneGroupNode.addChild(aLgt);
+ // scope ambient light to the lw3d scene
+ aLgt.addScope(sceneGroupNode);
+ scene.addLightNode(aLgt);
+ }
+
+ /**
+ * Add any defined lights to the java3d scene
+ */
+ void addLights() {
+ // Add lights to the scene
+ for (Enumeration e1 = lightList.elements(); e1.hasMoreElements(); ) {
+
+ debugOutputLn(LINE_TRACE, "adding light to scene group");
+ LwsLight light = (LwsLight)e1.nextElement();
+
+ if (light.getObjectNode() != null) {
+ // scope light to the lw3d scene
+ light.getLight().addScope(sceneGroupNode);
+
+ if (light.getParent() != -1) {
+ LwsObject parent = (LwsObject)
+ objectList.elementAt(light.getParent() - 1);
+ parent.addChild(light);
+ }
+ else { // No parent - add to scene group
+ sceneGroupNode.addChild(light.getObjectNode());
+
+ }
+
+ // collect behaviors if LOAD_BEHAVIOR_NODES is set
+ if (loadBehaviors != 0) {
+ if (!(light.getObjectBehaviors()).isEmpty())
+ sceneBehaviors.addAll(light.getObjectBehaviors());
+
+ }
+
+ scene.addLightNode(light.getLight());
+ }
+ else
+ debugOutputLn(LINE_TRACE, "light object null?");
+ }
+ }
+
+ /**
+ * Adds the Camera's transform group to the scene, either by parenting
+ * it to the appropriate object or by adding it to the scene root.
+ * To use this camera data, users can request the camera/view data
+ * for the scene and can then insert a ViewPlatform in the transform group.
+ */
+ void addCamera() {
+ // Add camera effects to scene.
+ if (camera != null) {
+ if (camera.getParent() != -1) {
+ debugOutputLn(VALUES, "camera parent = " +
+ camera.getParent());
+ LwsObject parent = (LwsObject)
+ objectList.elementAt(camera.getParent() - 1);
+ parent.addChild(camera);
+ debugOutputLn(VALUES, "added child successfully");
+ }
+ else {
+ sceneGroupNode.addChild(camera.getObjectNode());
+
+ }
+
+ // collect behaviors if LOAD_BEHAVIOR_NODES is set
+ if (loadBehaviors != 0) {
+ if (!(camera.getObjectBehaviors()).isEmpty())
+ sceneBehaviors.addAll(camera.getObjectBehaviors());
+ }
+
+ scene.addViewGroup(camera.getObjectNode());
+ }
+ }
+
+ /**
+ * Add appropriate fog effects to the scene
+ */
+ void addFog() {
+ if (fog != null) {
+ Fog fogNode = fog.getObjectNode();
+ if (fogNode != null) {
+ sceneGroupNode.addChild(fogNode);
+ scene.addFogNode(fogNode);
+ }
+ }
+ }
+
+ /**
+ * Add the behaviors to the scene
+ */
+ void addBehaviors() {
+ if (!sceneBehaviors.isEmpty()) {
+ Enumeration e = sceneBehaviors.elements();
+ while (e.hasMoreElements()) {
+ scene.addBehaviorNode((Behavior)e.nextElement());
+ }
+ }
+ }
+
+ /**
+ * Add appropriate background effects to the scene. Note that the java3d
+ * background may not have all of the information of the lw3d background,
+ * as the loader does not currently process items such as gradients between
+ * the horizon and sky colors
+ */
+ void addBackground() {
+ if (background != null) {
+ Background bgNode = background.getObjectNode();
+ if (bgNode != null) {
+ sceneGroupNode.addChild(bgNode);
+ scene.addBackgroundNode(bgNode);
+ }
+ }
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwLightObject.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwLightObject.java
new file mode 100644
index 0000000..ce61df2
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwLightObject.java
@@ -0,0 +1,100 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import javax.vecmath.Color3f;
+import javax.media.j3d.Light;
+
+
+/**
+ * This class is used to set the Light Intensity value according to the
+ * keyframe value of the envelope for that light. The class is used in
+ * conjunction with LightIntensityPathInterpolator, which uses this
+ * class as the target of its interpolations.
+ */
+
+class LwLightObject {
+
+ float intensity;
+ Color3f color;
+ Light theLight;
+
+ LwLightObject(Light theLight, float intensity, Color3f color) {
+ this.intensity = intensity;
+ this.color = color;
+ this.theLight = theLight;
+ }
+
+ void setIntensity(float intensity) {
+ Color3f newLightColor = new Color3f(color.x * intensity,
+ color.y * intensity,
+ color.z * intensity);
+ if (theLight != null)
+ theLight.setColor(newLightColor);
+ this.intensity = intensity;
+ }
+
+ void setColor(Color3f color) {
+ Color3f newLightColor = new Color3f(color.x * intensity,
+ color.y * intensity,
+ color.z * intensity);
+ if (theLight != null)
+ theLight.setColor(newLightColor);
+ this.color = color;
+ }
+
+ void setLight(Light l) {
+ theLight = l;
+ Color3f newLightColor = new Color3f(color.x * intensity,
+ color.y * intensity,
+ color.z * intensity);
+ if (theLight != null)
+ theLight.setColor(newLightColor);
+ }
+}
+
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwoParser.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwoParser.java
new file mode 100644
index 0000000..40f2df0
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwoParser.java
@@ -0,0 +1,424 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Vector;
+import java.util.Date;
+import java.util.Enumeration;
+import com.sun.j3d.loaders.lw3d.LWOBFileReader;
+import com.sun.j3d.internal.J3dUtilsI18N;
+import java.net.*;
+import com.sun.j3d.loaders.ParsingErrorException;
+import com.sun.j3d.loaders.IncorrectFormatException;
+
+
+/**
+ * This class is responsible for parsing a binary Object file and storing
+ * the data. This class is not called directly, but is the parent class of
+ * J3dLwoObject. The subclass calls this class to parse the file, then it
+ * turns the resulting data into Java3D objects. LwoObject instantiates
+ * an LWOBFileReader object to parse the data. Then the class creates a
+ * list of ShapeHolder objects to hold all of the vertex/facet data and
+ * surface references and creates a list of LwoSurface objects to hold
+ * the data for each surface.<BR>
+ * Rather than describe in detail how the file is parsed for each method,
+ * I advise the user of this code to understand the lw3d file format
+ * specs, which are pretty clear.
+ */
+
+class LwoParser extends ParserObject {
+
+ LWOBFileReader theReader;
+ int currLength;
+ float coordsArray[];
+ float normalCoordsArray[];
+ int facetIndicesArray[];
+ int facetSizesArray[];
+ int normalIndicesArray[];
+ int red = 255, green = 255, blue = 255;
+ float diffuse = 0.0f, specular = 0.0f, transparency = 0.0f, luminosity = 0.0f;
+ int gloss = 128;
+ Vector surfNameList = null;
+ Vector surfaceList = new Vector(200);
+ Vector shapeList = new Vector(200);
+
+ /**
+ * Constructor: Creates file reader and calls parseFile() to actually
+ * read the file and grab the data
+ */
+ LwoParser(String fileName, int debugVals)
+ throws FileNotFoundException {
+
+ super(debugVals);
+ debugOutputLn(TRACE, "parser()");
+ long start = System.currentTimeMillis();
+ theReader = new LWOBFileReader(fileName);
+ debugOutputLn(TIME, " file opened in " +
+ (System.currentTimeMillis() - start));
+ parseFile();
+ }
+
+ LwoParser(URL url, int debugVals)
+ throws FileNotFoundException {
+ super(debugVals);
+ debugOutputLn(TRACE, "parser()");
+ try {
+ long start = System.currentTimeMillis();
+ theReader = new LWOBFileReader(url);
+ debugOutputLn(TIME, " file opened in " +
+ (System.currentTimeMillis() - start));
+ }
+ catch (IOException ex) {
+ throw new FileNotFoundException(url.toString());
+ }
+ parseFile();
+ }
+
+
+ /**
+ * Detail polygons are currently not implemented by this loader. Their
+ * structure in geometry files is a bit complex, so there's this separate
+ * method for simply parsing through and ignoring the data for detail
+ * polygons
+ */
+ int skipDetailPolygons(int numPolys) throws ParsingErrorException {
+ debugOutputLn(TRACE, "skipDetailPolygons(), numPolys = " + numPolys);
+ int lengthRead = 0;
+ int vert;
+
+ try {
+ for (int polyNum = 0; polyNum < numPolys; ++polyNum) {
+ debugOutputLn(VALUES, "polyNum = " + polyNum);
+ int numVerts = theReader.getShortInt();
+ theReader.skip(numVerts * 2 + 2); // skip indices plus surf
+ lengthRead += (numVerts * 2) + 4; // increment counter
+ }
+ }
+ catch (IOException e) {
+ debugOutputLn(EXCEPTION, "Exception in reading detail polys: " + e);
+ throw new ParsingErrorException(e.getMessage());
+ }
+ return lengthRead;
+ }
+
+ /**
+ * Returns already-existing ShapeHolder if one exists with the same
+ * surface and the same geometry type (point, line, or poly)
+ */
+ ShapeHolder getAppropriateShape(int numSurf, int numVerts) {
+ for (Enumeration e = shapeList.elements();
+ e.hasMoreElements() ;) {
+ ShapeHolder shape = (ShapeHolder)e.nextElement();
+ if (shape.numSurf == numSurf)
+ if (shape.numVerts == numVerts ||
+ (shape.numVerts > 3 &&
+ numVerts > 3))
+ return shape;
+ }
+ return null;
+ }
+
+
+ /**
+ * Parse the file for all the data for a POLS object (polygon
+ * description)
+ */
+ void getPols(int length) {
+ debugOutputLn(TRACE, "getPols(len), len = " + length);
+ int vert;
+ int lengthRead = 0;
+ int prevNumVerts = -1;
+ int prevNumSurf = 0;
+ Vector facetSizesList;
+ int facetIndicesArray[];
+ facetSizesList =
+ new Vector(length/6); // worst case size (every poly one vert)
+ // Note that our array sizes are hardcoded because we don't
+ // know until we're done how large they will be
+ facetIndicesArray = new int[length/2];
+ ShapeHolder shape = new ShapeHolder(debugPrinter.getValidOutput());
+ debugOutputLn(VALUES, "new shape = " + shape);
+ shape.coordsArray = coordsArray;
+ shape.facetSizesList = facetSizesList;
+ //shape.facetIndicesList = facetIndicesList;
+ shape.facetIndicesArray = facetIndicesArray;
+ shapeList.addElement(shape);
+
+ //long startTime = (new Date()).getTime();
+ boolean firstTime = true;
+ while (lengthRead < length) {
+ int numVerts = theReader.getShortInt();
+ lengthRead += 2;
+ int intArray[] = new int[numVerts];
+ for (int i = 0; i < numVerts; ++i) {
+ intArray[i] = theReader.getShortInt();
+ lengthRead += 2;
+ }
+
+ int numSurf = theReader.getShortInt();
+ lengthRead += 2;
+ long startTimeBuff = 0, startTimeList = 0;
+ if (!firstTime &&
+ (numSurf != prevNumSurf ||
+ ((numVerts != prevNumVerts) &&
+ ((prevNumVerts < 3) ||
+ (numVerts < 3))))) {
+ // If above true, then start new shape
+ shape = getAppropriateShape(numSurf, numVerts);
+ if (shape == null) {
+ //debugOutputLn(LINE_TRACE, "Starting new shape");
+ facetSizesList = new Vector(length/6);
+ facetIndicesArray = new int[length/2];
+ shape = new ShapeHolder(debugPrinter.getValidOutput());
+ shape.coordsArray = coordsArray;
+ shape.facetSizesList = facetSizesList;
+ //shape.facetIndicesList = facetIndicesList;
+ shape.facetIndicesArray = facetIndicesArray;
+ shape.numSurf = numSurf;
+ shape.numVerts = numVerts;
+ shapeList.addElement(shape);
+ }
+ else {
+ facetSizesList = shape.facetSizesList;
+ facetIndicesArray = shape.facetIndicesArray;
+ }
+ }
+ else {
+ shape.numSurf = numSurf;
+ shape.numVerts = numVerts;
+ }
+ prevNumVerts = numVerts;
+ prevNumSurf = numSurf;
+ facetSizesList.addElement(new Integer(numVerts));
+
+ int currPtr = 0;
+ System.arraycopy(intArray, 0,
+ facetIndicesArray, shape.currentNumIndices,
+ numVerts);
+ shape.currentNumIndices += numVerts;
+ if (numSurf < 0) { // neg number means detail poly
+ int numPolys = theReader.getShortInt();
+ lengthRead += skipDetailPolygons(numPolys);
+ shape.numSurf = ~shape.numSurf & 0xffff;
+ if (shape.numSurf == 0)
+ shape.numSurf = 1; // Can't have surface = 0
+ }
+ firstTime = false;
+ }
+ }
+
+ /**
+ * Parses file to get the names of all surfaces. Each polygon will
+ * be associated with a particular surface number, which is the index
+ * number of these names
+ */
+ void getSrfs(int length) {
+ String surfName = new String();
+ surfNameList = new Vector(length/2); // worst case size (each name 2 chars long)
+ int lengthRead = 0;
+ int stopMarker = theReader.getMarker() + length;
+
+ int surfIndex = 0;
+ while (theReader.getMarker() < stopMarker) {
+ debugOutputLn(VALUES, "marker, stop = " +
+ theReader.getMarker() + ", " + stopMarker);
+ debugOutputLn(LINE_TRACE, "About to call getString");
+ surfName = theReader.getString();
+ debugOutputLn(VALUES, "Surfname = " + surfName);
+ surfNameList.addElement(surfName);
+ }
+ }
+
+ /**
+ * Parses file to get all vertices
+ */
+ void getPnts(int length) throws ParsingErrorException {
+ int numVerts = length / 12;
+ float x, y, z;
+
+ coordsArray = new float[numVerts*3];
+ theReader.getVerts(coordsArray, numVerts);
+ }
+
+ /**
+ * Creates new LwoSurface object that parses file and gets all
+ * surface parameters for a particular surface
+ */
+ void getSurf(int length) throws FileNotFoundException {
+ debugOutputLn(TRACE, "getSurf()");
+
+ // Create LwoSurface object to read and hold each surface, then
+ // store that surface in a vector of all surfaces.
+
+ LwoSurface surf = new LwoSurface(theReader, length,
+ debugPrinter.getValidOutput());
+ surfaceList.addElement(surf);
+ }
+
+
+ /**
+ * parses entire file.
+ * return -1 on error or 0 on completion
+ */
+ int parseFile() throws FileNotFoundException, IncorrectFormatException {
+ debugOutputLn(TRACE, "parseFile()");
+ int length = 0;
+ int lengthRead = 0;
+ int fileLength = 100000;
+
+ long loopStartTime = System.currentTimeMillis();
+ // Every parsing unit begins with a four character string
+ String tokenString = theReader.getToken();
+
+ while (!(tokenString == null) &&
+ lengthRead < fileLength) {
+ long startTime = System.currentTimeMillis();
+ // Based on value of tokenString, go to correct parsing method
+ length = theReader.getInt();
+
+ lengthRead += 4;
+ //debugOutputLn(VALUES, "length, lengthRead, fileLength = " +
+ // length + ", " + lengthRead + ", " + fileLength);
+ //debugOutputLn(VALUES, "LWOB marker is at: " + theReader.getMarker());
+
+ if (tokenString.equals("FORM")) {
+ //debugOutputLn(LINE_TRACE, "got a form");
+ fileLength = length + 4;
+ length = 0;
+ tokenString = theReader.getToken();
+ lengthRead += 4;
+ if (!tokenString.equals("LWOB"))
+ throw new IncorrectFormatException(
+ "File not of FORM-length-LWOB format");
+ }
+ else if (tokenString.equals("PNTS")) {
+ //debugOutputLn(LINE_TRACE, "PNTS");
+ getPnts(length);
+ debugOutputLn(TIME, "done with " + tokenString + " in " +
+ (System.currentTimeMillis() - startTime));
+ }
+ else if (tokenString.equals("POLS")) {
+ //debugOutputLn(LINE_TRACE, "POLS");
+ getPols(length);
+ debugOutputLn(TIME, "done with " + tokenString + " in " +
+ (System.currentTimeMillis() - startTime));
+ }
+ else if (tokenString.equals("SRFS")) {
+ //debugOutputLn(LINE_TRACE, "SRFS");
+ getSrfs(length);
+ debugOutputLn(TIME, "done with " + tokenString + " in " +
+ (System.currentTimeMillis() - startTime));
+ }
+ else if (tokenString.equals("CRVS")) {
+ //debugOutputLn(LINE_TRACE, "CRVS");
+ theReader.skipLength(length);
+ //debugOutputLn(TIME, "done with " + tokenString + " in " +
+ // (System.currentTimeMillis() - startTime));
+ }
+ else if (tokenString.equals("PCHS")) {
+ //debugOutputLn(LINE_TRACE, "PCHS");
+ theReader.skipLength(length);
+ //debugOutputLn(TIME, "done with " + tokenString + " in " +
+ // (System.currentTimeMillis() - startTime));
+ }
+ else if (tokenString.equals("SURF")) {
+ //debugOutputLn(LINE_TRACE, "SURF");
+ getSurf(length);
+ //debugOutputLn(VALUES, "Done with SURF, marker = " + theReader.getMarker());
+ debugOutputLn(TIME, "done with " + tokenString + " in " +
+ (System.currentTimeMillis() - startTime));
+ }
+ else if (tokenString.equals("LWOB")) {
+ //debugOutputLn(LINE_TRACE, "LWOB");
+ }
+ else {
+ //debugOutputLn(LINE_TRACE, "Unknown object = " + tokenString);
+ theReader.skipLength(length);
+ //debugOutputLn(TIME, "done with " + tokenString + " in " +
+ // (System.currentTimeMillis() - startTime));
+ }
+ lengthRead += length;
+ if (lengthRead < fileLength) {
+ //debugOutputLn(VALUES, "end of parseFile, length, lengthRead = " +
+ // length + ", " + lengthRead);
+ tokenString = theReader.getToken();
+ lengthRead += 4;
+ //debugOutputLn(VALUES, "just got tokenString = " + tokenString);
+ }
+ }
+ debugOutputLn(TIME, "done with parseFile in " +
+ (System.currentTimeMillis() - loopStartTime));
+ return 0;
+ }
+
+ /**
+ * This method is used only for testing
+ */
+ static void main(String[] args) {
+ String fileName;
+ if (args.length == 0)
+ fileName = "cube.obj";
+ else
+ fileName = args[0];
+
+ try {
+ LwoParser theParser = new LwoParser(fileName, 0);
+ }
+ catch (FileNotFoundException e) {
+ System.err.println(e.getMessage());
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+}
+
+
+
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwoSurface.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwoSurface.java
new file mode 100644
index 0000000..813b5ad
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwoSurface.java
@@ -0,0 +1,316 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.awt.Image;
+import java.io.IOException;
+import java.util.Vector;
+import java.util.Enumeration;
+import javax.vecmath.Color3f;
+import javax.vecmath.Vector3f;
+import com.sun.j3d.loaders.lw3d.LWOBFileReader;
+import com.sun.j3d.internal.J3dUtilsI18N;
+import java.io.FileNotFoundException;
+import com.sun.j3d.loaders.IncorrectFormatException;
+import com.sun.j3d.loaders.ParsingErrorException;
+
+
+/**
+ * This class is responsible for retrieving the surface parameters for a
+ * particular surface from a binary Object file and turning that data
+ * into Java3D data. These surface parameters include
+ * diffuse/specular/emissive properties, color, shininess, transparency,
+ * and textures. For textures, this class instantiates a LwoTexture object
+ * to parse that data and turn it into Java3D texture data.
+ */
+
+class LwoSurface extends ParserObject {
+
+ LWOBFileReader theReader;
+ int red = 255, green = 255, blue = 255;
+ float diffuse = 0.0f, specular = 0.0f, transparency = 0.0f, luminosity = 0.0f;
+ float creaseAngle = 0.0f;
+ int gloss = 128;
+ Color3f color, diffuseColor, specularColor, emissiveColor;
+ float shininess;
+ Image theImage = null;
+ Vector3f textureCenter = null, textureSize = null;
+ int textureAxis;
+ String surfName;
+ Vector textureList = new Vector();
+
+ /**
+ * Constructor that parses surface data from the binary file
+ * and creates the necessary Java3d objects
+ */
+ LwoSurface(LWOBFileReader reader, int length, int debugVals)
+ throws FileNotFoundException {
+
+ super(debugVals);
+ debugOutputLn(TRACE, "LwoSurface()");
+ theReader = reader;
+ getSurf(length);
+ setJ3dColors();
+ }
+
+ /**
+ * Creates Java3d color objects from the lw3d surface data
+ */
+ void setJ3dColors() {
+ color = new Color3f((float)red/(float)255,
+ (float)green/(float)255,
+ (float)blue/(float)255);
+ diffuseColor = new Color3f(diffuse*color.x,
+ diffuse*color.y,
+ diffuse*color.z);
+ specularColor = new Color3f(specular*color.x,
+ specular*color.y,
+ specular*color.z);
+ emissiveColor = new Color3f(luminosity*color.x,
+ luminosity*color.y,
+ luminosity*color.z);
+ shininess = (float)(128.0 * ((float)gloss/1024.0));
+ }
+
+ Color3f getColor() {
+ return color;
+ }
+
+ Color3f getDiffuseColor() {
+ return diffuseColor;
+ }
+
+ Color3f getSpecularColor() {
+ return specularColor;
+ }
+
+ Color3f getEmissiveColor() {
+ return emissiveColor;
+ }
+
+ float getShininess() {
+ return shininess;
+ }
+
+ float getCreaseAngle() {
+ return creaseAngle;
+ }
+
+ /**
+ * Returns the LwoTexture for the surface, if any is defined. Note that
+ * lw3d allows users to define multiple textures for any surface, which
+ * is not possible through Java3d. Therefore, we just grab the first
+ * texture in any list of textures for a surface
+ */
+ LwoTexture getTexture() {
+ debugOutputLn(TRACE, "getTexture()");
+ try {
+ if (textureList.isEmpty()) {
+ return null;
+ }
+ else {
+ return (LwoTexture)textureList.elementAt(0);
+ }
+ }
+ catch (ArrayIndexOutOfBoundsException e) {
+ debugOutputLn(EXCEPTION,
+ "getTexture(), exception returning first element: " + e);
+ return null;
+ }
+ }
+
+ String getSurfName() {
+ return surfName;
+ }
+
+ float getTransparency() {
+ return transparency;
+ }
+
+ /**
+ * Parses the binary file and gets all data for this surface
+ */
+ void getSurf(int length)
+ throws FileNotFoundException, IncorrectFormatException,
+ ParsingErrorException {
+
+ debugOutputLn(TRACE, "getSurf()");
+
+ // These "got*" booleans are to help use read the best version of
+ // the data - the float values for these parameters should take
+ // precedence over the other format
+ boolean gotLuminosityFloat = false;
+ boolean gotTransparencyFloat = false;
+ boolean gotDiffuseFloat = false;
+ boolean gotSpecularFloat = false;
+ int surfStopMarker = theReader.getMarker() + length;
+ surfName = theReader.getString();
+ String tokenString = theReader.getToken();
+ while (!(tokenString == null) &&
+ theReader.getMarker() < surfStopMarker) {
+ debugOutputLn(VALUES, " tokenString = " + tokenString);
+ debugOutputLn(VALUES, " marker, stop = " +
+ theReader.getMarker() + ", " + surfStopMarker);
+ String textureToken = null;
+ int fieldLength = theReader.getShortInt();
+ debugOutputLn(VALUES, " fl = " + fieldLength);
+
+ if (tokenString.equals("COLR")) {
+ debugOutputLn(LINE_TRACE, " COLR");
+ try {
+ red = theReader.read();
+ green = theReader.read();
+ blue = theReader.read();
+ theReader.read();
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ if (fieldLength != 4)
+ throw new IncorrectFormatException(
+ J3dUtilsI18N.getString("LwoSurface0"));
+ }
+ else if (tokenString.equals("FLAG")) {
+ debugOutputLn(LINE_TRACE, " FLAG");
+ theReader.skipLength(fieldLength);
+ }
+ else if (tokenString.equals("VLUM")) {
+ debugOutputLn(LINE_TRACE, " VLUM");
+ luminosity = theReader.getFloat();
+ gotLuminosityFloat = true;
+ }
+ else if (tokenString.equals("LUMI")) {
+ debugOutputLn(LINE_TRACE, " LUMI");
+ if (gotLuminosityFloat)
+ theReader.skipLength(fieldLength);
+ else
+ luminosity = (float)(theReader.getShortInt())/255;
+ }
+ else if (tokenString.equals("VDIF")) {
+ debugOutputLn(LINE_TRACE, " VDIF");
+ if (fieldLength != 4)
+ throw new IncorrectFormatException("VDIF problem");
+ diffuse = theReader.getFloat();
+ gotDiffuseFloat = true;
+ debugOutputLn(VALUES, "diff = " + diffuse);
+ }
+ else if (tokenString.equals("DIFF")) {
+ debugOutputLn(LINE_TRACE, " DIFF");
+ if (gotDiffuseFloat)
+ theReader.skipLength(fieldLength);
+ else
+ diffuse = (float)theReader.getShortInt()/255;
+ }
+ else if (tokenString.equals("VTRN")) {
+ debugOutputLn(LINE_TRACE, " VTRN");
+ transparency = theReader.getFloat();
+ gotTransparencyFloat = true;
+ }
+ else if (tokenString.equals("TRAN")) {
+ debugOutputLn(LINE_TRACE, " TRAN");
+ if (gotTransparencyFloat)
+ theReader.skipLength(fieldLength);
+ else
+ transparency = (float)theReader.getShortInt()/255;
+ }
+ else if (tokenString.equals("VSPC")) {
+ debugOutputLn(LINE_TRACE, " VSPC");
+ specular = theReader.getFloat();
+ gotSpecularFloat = true;
+ debugOutputLn(VALUES, "spec = " + specular);
+ }
+ else if (tokenString.equals("SPEC")) {
+ debugOutputLn(LINE_TRACE, " SPEC");
+ if (gotSpecularFloat)
+ theReader.skipLength(fieldLength);
+ else {
+ if (fieldLength == 4) // Bug in some LW versions
+ specular = (float)theReader.getInt()/255;
+ else
+ specular = (float)theReader.getShortInt()/255;
+ }
+ }
+ else if (tokenString.equals("GLOS")) {
+ debugOutputLn(LINE_TRACE, " GLOS");
+ if (fieldLength == 4)
+ gloss = theReader.getInt();
+ else
+ gloss = theReader.getShortInt();
+ }
+ else if (tokenString.equals("SMAN")) {
+ debugOutputLn(LINE_TRACE, " SMAN");
+ creaseAngle = theReader.getFloat();
+ }
+ else if (tokenString.endsWith("TEX")) {
+ // Textures are complex - hand off this bit to the
+ // LwoTexture class
+ LwoTexture texture =
+ new LwoTexture(theReader,
+ surfStopMarker - theReader.getMarker(),
+ tokenString,
+ debugPrinter.getValidOutput());
+ textureToken = texture.getNextToken();
+ if (texture.isHandled())
+ textureList.addElement(texture);
+ debugOutputLn(WARNING, "val = " + tokenString);
+ }
+ else {
+ debugOutputLn(WARNING,
+ "unrecognized token: " + tokenString);
+ theReader.skipLength(fieldLength);
+ }
+ if (theReader.getMarker() < surfStopMarker) {
+ if (textureToken == null)
+ tokenString = theReader.getToken();
+ else
+ tokenString = textureToken;
+ }
+ }
+ }
+
+}
+
+
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwoTexture.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwoTexture.java
new file mode 100644
index 0000000..57bbf7b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwoTexture.java
@@ -0,0 +1,284 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.awt.Component;
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.io.FileReader;
+import java.io.File;
+import java.io.IOException;
+import java.util.Vector;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import javax.vecmath.Color3f;
+import javax.vecmath.Vector3f;
+import com.sun.j3d.utils.image.TextureLoader;
+import javax.media.j3d.Texture;
+import javax.media.j3d.Texture2D;
+import javax.media.j3d.ImageComponent;
+import javax.media.j3d.ImageComponent2D;
+import com.sun.j3d.loaders.lw3d.LWOBFileReader;
+import java.io.FileNotFoundException;
+import com.sun.j3d.loaders.ParsingErrorException;
+
+/**
+ * This class is responsible for parsing the binary data in an Object file
+ * that describes a texture for a particular surface and turning that data
+ * into Java3D texture data. If the texture is coming from a file (which
+ * is the only type of texture handled by the loader currently; other
+ * types of texture definitions are ignored), then this class instantiates
+ * a TargaReader object to read the data in that file. Once all of the
+ * data has been read, the class creates a Java3D Texture object by first
+ * scaling the image using the ImageScaler class (since all textures must
+ * have width/height = power of 2; Note: this functionality is now built
+ * into the TextureLoader class, so it could be removed from this loader)
+ * and then creating a Texture with that image.
+ */
+
+class LwoTexture extends ParserObject {
+
+ LWOBFileReader theReader;
+ int red = 255, green = 255, blue = 255;
+ Color3f color, diffuseColor, specularColor, emissiveColor;
+ Image theImage = null;
+ String imageFile = null;
+ Vector3f textureSize = new Vector3f(1f, 1f, 1f);;
+ Vector3f textureCenter = new Vector3f(0f, 0f, 0f);
+ int textureAxis;
+ int flags = 0;
+ String type;
+ String mappingType;
+ String nextToken = null;
+ static Hashtable imageTable = new Hashtable();
+ static Hashtable textureTable = new Hashtable();
+
+ /**
+ * Constructor: calls readTexture() to parse the file and retrieve
+ * texture parameters
+ */
+ LwoTexture(LWOBFileReader reader, int length, String typename,
+ int debugVals) throws FileNotFoundException {
+ super(debugVals);
+ debugOutputLn(TRACE, "Constructor");
+ theReader = reader;
+ type = typename;
+ readTexture(length);
+ }
+
+ String getNextToken() {
+ return nextToken;
+ }
+
+ /**
+ * The loader currently only handles CTEX and DTEX texture types
+ * (These either represent the surface color like a decal (CTEX)
+ * or modify the diffuse color (DTEX)
+ */
+ boolean isHandled() {
+ if ((type.equals("CTEX") ||
+ type.equals("DTEX")) &&
+ theImage != null)
+ return true;
+ debugOutputLn(LINE_TRACE, "failed isHandled(), type, theImage = " +
+ type + ", " + theImage);
+ return false;
+ }
+
+ /**
+ * Return the actual Texture object associated with the current image.
+ * If we've already created a texture for this image, return that;
+ * otherwise create a new Texture
+ */
+ Texture getTexture() {
+ debugOutputLn(TRACE, "getTexture()");
+ if (theImage == null)
+ return null;
+ Texture2D t2d = (Texture2D)textureTable.get(theImage);
+ if (t2d == null) {
+ ImageScaler scaler = new ImageScaler((BufferedImage)theImage);
+ BufferedImage scaledImage = (BufferedImage)scaler.getScaledImage();
+ TextureLoader tl = new TextureLoader(scaledImage);
+ t2d = (Texture2D)tl.getTexture();
+ textureTable.put(theImage, t2d);
+ }
+
+ return t2d;
+ }
+
+ String getType() {
+ return type;
+ }
+
+ Color3f getColor() {
+ return color;
+ }
+
+ Image getImage() {
+ return theImage;
+ }
+
+ Vector3f getTextureSize() {
+ return textureSize;
+ }
+
+ int getTextureAxis() {
+ return textureAxis;
+ }
+
+ Vector3f getTextureCenter() {
+ return textureCenter;
+ }
+
+ String getMappingType() {
+ return mappingType;
+ }
+
+ /**
+ * Parse the binary file to retrieve all texture parameters for this
+ * surface. If/when we encounter a TIMG parameter, which contains the
+ * filename of an image, then create a new TargaReader object to
+ * read that image file
+ */
+ void readTexture(int length)
+ throws FileNotFoundException, ParsingErrorException {
+
+ debugOutputLn(TRACE, "readTexture()");
+
+ int surfStopMarker = theReader.getMarker() + length;
+ mappingType = theReader.getString();
+ debugOutputLn(VALUES, "mappingType = " + mappingType);
+ String tokenString = theReader.getToken();
+ while (!(tokenString == null) && theReader.getMarker() < surfStopMarker) {
+
+ debugOutputLn(VALUES, " tokenString = " + tokenString);
+ debugOutputLn(VALUES, " marker, stop = " + theReader.getMarker() + ", " + surfStopMarker);
+
+ if (tokenString.endsWith("TEX") ||
+ (!tokenString.startsWith("T") || tokenString.equals("TRAN"))) {
+ nextToken = tokenString;
+ return;
+ }
+
+ int fieldLength = theReader.getShortInt();
+ debugOutputLn(VALUES, " fl = " + fieldLength);
+
+ if (tokenString.equals("TFLG")) {
+ debugOutputLn(WARNING, "Not yet handling: " + tokenString);
+ flags = theReader.getShortInt();
+ textureAxis = flags & 0x07;
+ debugOutputLn(WARNING, "val = " + flags);
+ }
+ else if (tokenString.equals("TCLR")) {
+ debugOutputLn(WARNING, "Not yet handling: " + tokenString);
+ try {
+ red = theReader.read();
+ green = theReader.read();
+ blue = theReader.read();
+ theReader.read();
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ debugOutputLn(WARNING, "val = " + red + ", " + green +
+ ", " + blue);
+ }
+ else if (tokenString.equals("TIMG")) {
+ debugOutputLn(WARNING, "Not yet handling: " + tokenString);
+ imageFile = theReader.getString();
+ debugOutputLn(VALUES, "imageFile = " + imageFile);
+ if (imageFile.indexOf("none") == -1) {
+ if ((theImage =
+ (Image)imageTable.get(imageFile)) == null) {
+ try {
+ TargaReader tr =
+ new TargaReader(imageFile,
+ debugPrinter.getValidOutput());
+ theImage = tr.getImage();
+ imageTable.put(imageFile, theImage);
+ }
+ catch (FileNotFoundException e) {
+ // Ignore texture if can't find it
+ debugOutputLn(WARNING, "Image File skipped: " +
+ imageFile);
+ }
+ }
+ }
+ debugOutputLn(WARNING, "val = __" + imageFile + "__");
+ }
+ else if (tokenString.equals("TWRP")) {
+ debugOutputLn(WARNING, "Not yet handling: " + tokenString);
+ int widthWrap = theReader.getShortInt();
+ int heightWrap = theReader.getShortInt();
+ debugOutputLn(WARNING, "val = " + widthWrap + ", " +
+ heightWrap);
+ }
+ else if (tokenString.equals("TCTR")) {
+ debugOutputLn(WARNING, "Not yet handling: " + tokenString);
+ textureCenter.x = theReader.getFloat();
+ textureCenter.y = theReader.getFloat();
+ textureCenter.z = theReader.getFloat();
+ debugOutputLn(WARNING, "val = " + textureCenter);
+ }
+ else if (tokenString.equals("TSIZ")) {
+ debugOutputLn(WARNING, "Not yet handling: " + tokenString);
+ textureSize.x = theReader.getFloat();
+ textureSize.y = theReader.getFloat();
+ textureSize.z = theReader.getFloat();
+ debugOutputLn(WARNING, "val = " + textureSize);
+ }
+ else {
+ debugOutputLn(WARNING,
+ "unrecognized token: " + tokenString);
+ theReader.skipLength(fieldLength);
+ }
+ if (theReader.getMarker() < surfStopMarker) {
+ tokenString = theReader.getToken();
+ }
+ }
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsBackground.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsBackground.java
new file mode 100644
index 0000000..559857c
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsBackground.java
@@ -0,0 +1,163 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import java.io.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import java.util.Enumeration;
+import com.sun.j3d.loaders.ParsingErrorException;
+
+
+/**
+ * This class creates a Background object (solid color only, no geometry)
+ * according to some of the data stored in a Scene file. Note: Lightwave
+ * defines much more complex backgrounds that the loader currently
+ * handles. It should be possible to se Background Geometry to handle
+ * most of these cases, if there's time and will to work on the problem.
+ */
+
+class LwsBackground extends TextfileParser {
+
+ // data from the file
+ int solidBackdrop;
+ Color3f color, zenithColor, skyColor, groundColor, nadirColor;
+ Background backgroundObject = null;
+
+
+ /**
+ * Constructor: parses stream and retrieves all Background-related data
+ */
+ LwsBackground(StreamTokenizer st, int debugVals)
+ throws ParsingErrorException {
+
+ debugPrinter.setValidOutput(debugVals);
+ debugOutput(TRACE, "LwsBackground()");
+ color = new Color3f(0f, 0f, 0f);
+ zenithColor = new Color3f(0f, 0f, 0f);
+ skyColor = new Color3f(0f, 0f, 0f);
+ groundColor = new Color3f(0f, 0f, 0f);
+ nadirColor = new Color3f(0f, 0f, 0f);
+
+ solidBackdrop = (int)getNumber(st);
+ while (!isCurrentToken(st, "FogType")) {
+ debugOutputLn(LINE_TRACE, "currentToken = " + st.sval);
+
+ if (isCurrentToken(st, "BackdropColor")) {
+ color.x = (float)getNumber(st)/255f;
+ color.y = (float)getNumber(st)/255f;
+ color.z = (float)getNumber(st)/255f;
+ }
+ else if (isCurrentToken(st, "NadirColor")) {
+ nadirColor.x = (float)getNumber(st)/255f;
+ nadirColor.y = (float)getNumber(st)/255f;
+ nadirColor.z = (float)getNumber(st)/255f;
+ }
+ else if (isCurrentToken(st, "SkyColor")) {
+ skyColor.x = (float)getNumber(st)/255f;
+ skyColor.y = (float)getNumber(st)/255f;
+ skyColor.z = (float)getNumber(st)/255f;
+ }
+ else if (isCurrentToken(st, "GroundColor")) {
+ groundColor.x = (float)getNumber(st)/255f;
+ groundColor.y = (float)getNumber(st)/255f;
+ groundColor.z = (float)getNumber(st)/255f;
+ }
+ else if (isCurrentToken(st, "NadirColor")) {
+ nadirColor.x = (float)getNumber(st)/255f;
+ nadirColor.y = (float)getNumber(st)/255f;
+ nadirColor.z = (float)getNumber(st)/255f;
+ }
+ try {
+ st.nextToken();
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+ st.pushBack(); // push token back on stack
+ }
+
+ /**
+ * Creates Java3d objects from the background data. Note that there
+ * are plenty of lw3d background attributes that the loader currently
+ * ignores. Some of these may best be handled by creating background
+ * geometry rather than a solid background color
+ */
+ void createJava3dObject() {
+ // TODO: there are various attributes of
+ // backdrops that we're not currently handling. In
+ // particular, if the file calls for a gradient background
+ // (solidBackdrop == 0), we ignore the request and just
+ // create a solid background from the sky color instead.
+ // We should be able to do something with the
+ // various colors specified, perhaps by creating
+ // background geometry with the appropriate vertex
+ // colors?
+
+ if (solidBackdrop != 0) {
+ backgroundObject = new Background(color);
+ debugOutput(VALUES, "Background color = " + color);
+ }
+ else {
+ backgroundObject = new Background(skyColor);
+ debugOutput(VALUES, "Background color = " + skyColor);
+ }
+ BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100000.0);
+ backgroundObject.setApplicationBounds(bounds);
+ }
+
+ Background getObjectNode() {
+ return backgroundObject;
+ }
+
+ void printVals() {
+ debugOutputLn(VALUES, " BACKGROUND vals: ");
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsCamera.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsCamera.java
new file mode 100644
index 0000000..afc2a53
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsCamera.java
@@ -0,0 +1,179 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import java.io.*;
+import java.util.Vector;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import java.util.Enumeration;
+import com.sun.j3d.loaders.ParsingErrorException;
+
+/**
+ * This class parses the data in a Scene file related to the camera and
+ * creates Java3D TransformGroup that holds the data for positioning
+ * and orienting the view according to the camera specifications.
+ */
+
+class LwsCamera extends TextfileParser implements LwsPrimitive {
+
+ // data from the file
+ String fileName;
+ String objName;
+ LwsMotion motion;
+ int parent;
+ TransformGroup objectTransform;
+ Vector objectBehavior;
+
+ /**
+ * Constructor: parses camera info and creates LwsMotion object for
+ * keyframe data
+ */
+ LwsCamera(StreamTokenizer st, int firstFrame,
+ int totalFrames, float totalTime,
+ int debugVals) throws ParsingErrorException {
+ debugPrinter.setValidOutput(debugVals);
+ parent = -1;
+ getNumber(st); // Skip ShowCamera parameters
+ getNumber(st);
+ getAndCheckString(st, "CameraMotion");
+ motion = new LwsMotion(st, firstFrame, totalFrames, totalTime,
+ debugPrinter.getValidOutput());
+
+ // TODO: buggy way to stop processing the camera. Should actually
+ // process required/optional fields in order and stop when there's
+ // no more.
+
+ while (!isCurrentToken(st, "DepthOfField")) {
+ debugOutputLn(LINE_TRACE, "currentToken = " + st.sval);
+
+ if (isCurrentToken(st, "ParentObject")) {
+ parent = (int)getNumber(st);
+ }
+ try {
+ st.nextToken();
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+ getNumber(st); // skip shadow type parameter
+ }
+
+ /**
+ * Returns parent of the camera object
+ */
+ int getParent() {
+ return parent;
+ }
+
+ /**
+ * Creates Java3D items from the camera data. These objects consist
+ * of: a TransformGroup to hold the view platform, and the behaviors
+ * (if any) that act upon the view's TransformGroup.
+ */
+ void createJava3dObject(int loadBehaviors)
+ {
+ Matrix4d mat = new Matrix4d();
+ mat.setIdentity();
+ // Set the node's transform matrix according to the first frame
+ // of the object's motion
+ LwsFrame firstFrame = motion.getFirstFrame();
+ firstFrame.setMatrix(mat);
+ debugOutputLn(VALUES, " Camera Matrix = \n" + mat);
+ Transform3D t1 = new Transform3D();
+ Matrix4d m = new Matrix4d();
+ double scale = .1;
+ m.setColumn(0, scale, 0, 0, 0); // setScale not yet implemented
+ m.setColumn(1, 0, scale, 0, 0);
+ m.setColumn(2, 0, 0, scale, 0);
+ m.setColumn(3, 0, 0, 0, 1);
+ Transform3D scaleTrans = new Transform3D(m);
+ TransformGroup scaleGroup = new TransformGroup(scaleTrans);
+ scaleGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+ scaleGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
+ // mat.mul(m);
+ t1.set(mat);
+ objectTransform = new TransformGroup(t1);
+ objectTransform.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+ objectBehavior = new Vector();;
+ if (loadBehaviors != 0) {
+ motion.createJava3dBehaviors(objectTransform);
+ Behavior b = motion.getBehaviors();
+ if (b != null)
+ objectBehavior.addElement(b);
+ }
+ }
+
+ /**
+ * Returns TransformGroup of camera
+ */
+ public TransformGroup getObjectNode()
+ {
+ return objectTransform;
+ }
+
+ /**
+ * Returns animation behaviors for camera
+ */
+ public Vector getObjectBehaviors()
+ {
+ debugOutputLn(TRACE, "getObjectBehaviors()");
+ return objectBehavior;
+ }
+
+ /**
+ * This is a debuggin utility, not currently activated. It prints
+ * out the camera values
+ */
+ void printVals()
+ {
+ System.out.println(" objName = " + objName);
+ motion.printVals();
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelope.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelope.java
new file mode 100644
index 0000000..e2b994e
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelope.java
@@ -0,0 +1,160 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.io.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import com.sun.j3d.internal.J3dUtilsI18N;
+import com.sun.j3d.loaders.ParsingErrorException;
+import com.sun.j3d.loaders.IncorrectFormatException;
+
+/**
+ * This class is a superclass for any implementation of envelopes; the
+ * only subclass currently is LwsEnvelopeLightIntensity. LwsEnvelope
+ * parses the data in a Scene file and extracts the envelope data.
+ */
+
+class LwsEnvelope extends TextfileParser {
+
+ // data from the file
+ String name;
+ LwsEnvelopeFrame frames[];
+ int numFrames;
+ int numChannels;
+ boolean loop;
+ float totalTime;
+ int totalFrames;
+ Behavior behaviors;
+
+ /**
+ * Constructor: calls getEnvelope() to parse the stream for the
+ * envelope data
+ */
+ LwsEnvelope(StreamTokenizer st, int frames, float time) {
+ numFrames = 0;
+ totalTime = time;
+ totalFrames = frames;
+ name = getName(st);
+ getEnvelope(st);
+ }
+
+ /**
+ * Parses the stream to retrieve all envelope data. Creates
+ * LwsEnvelopeFrame objects for each keyframe of the envelope
+ * (these frames differ slightly from LwsFrame objects because
+ * envelopes contain slightly different data)
+ */
+ void getEnvelope(StreamTokenizer st)
+ throws IncorrectFormatException, ParsingErrorException
+ {
+ debugOutputLn(TRACE, "getEnvelope()");
+ numChannels = (int)getNumber(st);
+ if (numChannels != 1) {
+ throw new IncorrectFormatException(
+ J3dUtilsI18N.getString("LwsEnvelope0"));
+ }
+ debugOutputLn(LINE_TRACE, "got channels");
+
+ numFrames = (int)getNumber(st);
+ frames = new LwsEnvelopeFrame[numFrames];
+ debugOutputLn(VALUES, "got frames" + numFrames);
+
+ for (int i = 0; i < numFrames; ++i) {
+ frames[i] = new LwsEnvelopeFrame(st);
+ }
+ debugOutput(LINE_TRACE, "got all frames");
+
+ try {
+ st.nextToken();
+ while (!isCurrentToken(st, "EndBehavior")) {
+ // There is an undocumented "FrameOffset" in some files
+ st.nextToken();
+ }
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ int repeatVal = (int)getNumber(st);
+ if (repeatVal == 1)
+ loop = false;
+ else
+ loop = true;
+ }
+
+ /**
+ * This superclass does nothing here - if the loader understands
+ * how to deal with a particular type of envelope, it will use
+ * a subclass of LwsEnvelope that will override this method
+ */
+ void createJava3dBehaviors(TransformGroup target) {
+ behaviors = null;
+ }
+
+ Behavior getBehaviors() {
+ return behaviors;
+ }
+
+
+ LwsEnvelopeFrame getFirstFrame() {
+ if (numFrames > 0)
+ return frames[0];
+ else
+ return null;
+ }
+
+
+ void printVals() {
+ debugOutputLn(VALUES, " name = " + name);
+ debugOutputLn(VALUES, " numChannels = " + numChannels);
+ debugOutputLn(VALUES, " numFrames = " + numFrames);
+ debugOutputLn(VALUES, " loop = " + loop);
+ for (int i = 0; i < numFrames; ++i) {
+ debugOutputLn(VALUES, " FRAME " + i);
+ frames[i].printVals();
+ }
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelopeFrame.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelopeFrame.java
new file mode 100644
index 0000000..587175f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelopeFrame.java
@@ -0,0 +1,105 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import java.io.*;
+import javax.vecmath.Matrix4d;
+import javax.vecmath.Vector3d;
+import javax.vecmath.Point3f;
+
+/**
+ * This class represents one keyframe in an envelope sequence.
+ */
+
+class LwsEnvelopeFrame extends TextfileParser {
+
+ // data from the file
+ double value;
+ double frameNumber;
+ int linearValue;
+ double tension, continuity, bias;
+
+
+ /**
+ * Constructor: parses stream and stores data for one keyframe of
+ * an envelope sequence
+ */
+ LwsEnvelopeFrame(StreamTokenizer st) {
+ value = getNumber(st);
+ debugOutputLn(VALUES, "value = " + value);
+ frameNumber = (int)getNumber(st);
+ linearValue = (int)getNumber(st);
+ debugOutputLn(VALUES, "framenum, linear " + frameNumber + " , " + linearValue);
+ tension = getNumber(st);
+ continuity = getNumber(st);
+ bias = getNumber(st);
+ debugOutputLn(VALUES, "tension, cont, bias = " + tension + ", " + continuity + ", " + bias);
+ //System.out.println(" FRAME VALS");
+ //printVals();
+ }
+
+
+ double getValue() {
+ return value;
+ }
+
+
+ double getFrameNum() {
+ return frameNumber;
+ }
+
+
+ void printVals() {
+ debugOutputLn(VALUES, " value = " + value);
+ debugOutputLn(VALUES, " frameNum = " + frameNumber);
+ debugOutputLn(VALUES, " lin = " + linearValue);
+ debugOutputLn(VALUES, " tension = " + tension);
+ debugOutputLn(VALUES, " continuity = " + continuity);
+ debugOutputLn(VALUES, " bias = " + bias);
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelopeLightIntensity.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelopeLightIntensity.java
new file mode 100644
index 0000000..e241b5c
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelopeLightIntensity.java
@@ -0,0 +1,153 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import java.io.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import javax.media.j3d.TransformGroup;
+
+
+/**
+ * This class creates a LightIntensityPathInterpolator object from the
+ * keyframe-based envelope specified in a Scene file.
+ */
+
+class LwsEnvelopeLightIntensity extends LwsEnvelope {
+
+
+ /**
+ * Constructor: Calls superclass, which will parse the stream
+ * and store the envelope data
+ */
+ LwsEnvelopeLightIntensity(StreamTokenizer st,
+ int frames, float time) {
+ super(st, frames, time);
+ }
+
+ /**
+ * Creates Java3d behaviors given the stored envelope data. The
+ * Behavior created is a LightIntensityPathInterpolator
+ */
+ void createJava3dBehaviors(Object target) {
+ if (numFrames <= 1)
+ behaviors = null;
+ else {
+ long alphaAtOne = 0;
+ int loopCount;
+ if (loop)
+ loopCount = -1;
+ else
+ loopCount = 1;
+ // Note: hardcoded to always loop...
+ loopCount = -1;
+ debugOutputLn(VALUES, "totalTime = " + totalTime);
+ debugOutputLn(VALUES, "loopCount = " + loopCount);
+ float animTime = 1000.0f * totalTime *
+ (float)(frames[numFrames-1].getFrameNum()/(float)totalFrames);
+ debugOutputLn(VALUES, " anim time: " + animTime);
+ debugOutputLn(VALUES, " totalFrames = " + totalFrames);
+ debugOutputLn(VALUES, " lastFrame = " +
+ frames[numFrames-1].getFrameNum());
+ if (!loop)
+ alphaAtOne = (long)(1000.0*totalTime - animTime);
+ Alpha theAlpha =
+ new Alpha(loopCount, Alpha.INCREASING_ENABLE,
+ 0, 0, (long)animTime, 0,
+ alphaAtOne, 0, 0, 0);
+ float knots[] = new float[numFrames];
+ float values[] = new float[numFrames];
+ for (int i=0; i < numFrames; ++i) {
+ values[i] = (float)frames[i].getValue();
+ knots[i] = (float)(frames[i].getFrameNum())/
+ (float)(frames[numFrames-1].getFrameNum());
+ debugOutputLn(VALUES, "value, knot = " +
+ values[i] + ", " + knots[i]);
+ }
+ LightIntensityPathInterpolator l = new
+ LightIntensityPathInterpolator(theAlpha,
+ knots,
+ values,
+ target);
+ if (l != null) {
+ behaviors = l;
+ BoundingSphere bounds =
+ new BoundingSphere(new Point3d(0.0,0.0,0.0), 1000000.0);
+ behaviors.setSchedulingBounds(bounds);
+ ((TransformGroup)target).setCapability
+ (TransformGroup.ALLOW_TRANSFORM_WRITE);
+ ((TransformGroup)target).addChild(behaviors);
+ }
+ }
+ }
+
+
+ Behavior getBehaviors() {
+ return behaviors;
+ }
+
+
+ LwsEnvelopeFrame getFirstFrame() {
+ if (numFrames > 0)
+ return frames[0];
+ else
+ return null;
+ }
+
+
+ void printVals() {
+ debugOutputLn(VALUES, " name = " + name);
+ debugOutputLn(VALUES, " numChannels = " + numChannels);
+ debugOutputLn(VALUES, " numFrames = " + numFrames);
+ debugOutputLn(VALUES, " loop = " + loop);
+ for (int i = 0; i < numFrames; ++i) {
+ debugOutputLn(VALUES, " FRAME " + i);
+ frames[i].printVals();
+ }
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsFog.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsFog.java
new file mode 100644
index 0000000..2f8f83f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsFog.java
@@ -0,0 +1,143 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import java.io.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import java.util.Enumeration;
+import com.sun.j3d.loaders.ParsingErrorException;
+
+
+/**
+ * This class creates a Fog object from the data in a Scene file.
+ */
+
+class LwsFog extends TextfileParser {
+
+ // data from the file
+ float minDist, maxDist, minAmount, maxAmount;
+ int backdropFog;
+ Color3f color;
+ int type;
+ Fog fogObject = null;
+
+ /**
+ * Constructor: parses stream and stores fog data
+ */
+ LwsFog(StreamTokenizer st, int debugVals) throws ParsingErrorException {
+ debugPrinter.setValidOutput(debugVals);
+ debugOutput(TRACE, "LwsFog()");
+ color = new Color3f(0f, 0f, 0f);
+
+ while (!isCurrentToken(st, "DitherIntensity")) {
+ debugOutputLn(LINE_TRACE, "currentToken = " + st.sval);
+
+ if (isCurrentToken(st, "FogMinDist")) {
+ minDist = (float)getNumber(st);
+ }
+ else if (isCurrentToken(st, "FogMaxDist")) {
+ maxDist = (float)getNumber(st);
+ }
+ else if (isCurrentToken(st, "FogMinAmount")) {
+ minAmount = (float)getNumber(st);
+ }
+ else if (isCurrentToken(st, "FogMaxAmount")) {
+ maxAmount = (float)getNumber(st);
+ }
+ else if (isCurrentToken(st, "BackdropFog")) {
+ backdropFog = (int)getNumber(st);
+ }
+ else if (isCurrentToken(st, "FogColor")) {
+ color.x = (float)getNumber(st)/255f;
+ color.y = (float)getNumber(st)/255f;
+ color.z = (float)getNumber(st)/255f;
+ }
+ try {
+ st.nextToken();
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+ st.pushBack(); // push token back on stack
+ }
+
+ /**
+ * Creates Java3d Fog object given the fog parameters in the file.
+ * Note that various fog parameters in lw3d are not currently handled.
+ */
+ void createJava3dObject() {
+ // TODO: there are various attributes of lw fog that
+ // we're not currently handing, including non-linear fog
+ // (need to understand the two different types - these may
+ // map onto java3d's expontential fog node), non-solid
+ // backdrop colors (how to handle this?), min/max amount
+ // (j3d only handles 0 -> 1 case)
+
+ fogObject = new LinearFog(color, minDist, maxDist);
+ debugOutputLn(VALUES,
+ "just set linearFog with color, minDist, maxDist = " +
+ color + ", " +
+ minDist + ", " +
+ maxDist);
+ BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100000.0);
+ fogObject.setInfluencingBounds(bounds);
+ }
+
+ Fog getObjectNode()
+ {
+ return fogObject;
+ }
+
+ void printVals()
+ {
+ debugOutputLn(VALUES, " FOG vals: ");
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsFrame.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsFrame.java
new file mode 100644
index 0000000..f779eb4
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsFrame.java
@@ -0,0 +1,341 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import java.io.*;
+import javax.vecmath.Matrix4d;
+import javax.vecmath.Vector3d;
+import javax.vecmath.Point3f;
+
+/**
+ * This class is responsible for parsing the data in a Scene file
+ * associated with a single keyframe. This data includes the position,
+ * orientation, and scaling information, in addition to the frame number
+ * of that keyframe and some spline controls which are currently
+ * ignored.
+ */
+
+class LwsFrame extends TextfileParser {
+
+ // data from the file
+ double x, y, z;
+ double heading, pitch, bank;
+ double xScale, yScale, zScale;
+ double frameNumber;
+ int linearValue;
+ double tension, continuity, bias;
+
+ /**
+ * Constructor: parses and stores all data associated with a particular
+ * keyframe
+ */
+ LwsFrame(StreamTokenizer st) {
+ x = getNumber(st);
+ y = getNumber(st);
+ z = -getNumber(st);
+ debugOutputLn(VALUES, "x, y, z " + x + ", " + y + ", " + z);
+ heading = getNumber(st);
+ pitch = getNumber(st);
+ bank = getNumber(st);
+ debugOutputLn(VALUES, "(degrees) h, p, b = " + heading + ", " + pitch + ", " + bank);
+ heading *= (Math.PI / 180.0); // Java3d works with radians
+ pitch *= (Math.PI / 180.0);
+ bank *= (Math.PI / 180.0);
+ debugOutputLn(VALUES, "(radians) h, p, b = " + heading + ", " + pitch + ", " + bank);
+ debugOutputLn(LINE_TRACE, "got pos and ori");
+ xScale = getNumber(st);
+ yScale = getNumber(st);
+ zScale = getNumber(st);
+ debugOutputLn(VALUES, "xs, ys, zs " + xScale +", " + yScale + ", " + zScale);
+ frameNumber = (int)getNumber(st);
+ // Note: The following spline controls are ignored
+ linearValue = (int)getNumber(st);
+ debugOutputLn(VALUES, "framenum, linear " + frameNumber + " , " + linearValue);
+ tension = getNumber(st);
+ continuity = getNumber(st);
+ bias = getNumber(st);
+ debugOutputLn(VALUES, "tension, cont, bias = " + tension + ", " + continuity + ", " + bias);
+ }
+
+
+
+ /**
+ * Construct new frame that's in-between two given frames
+ * Ratio gives the interpolation value for how far in-between
+ * the new frame should be (0.5 is half-way, etc)
+ */
+ LwsFrame(LwsFrame prevFrame, LwsFrame nextFrame, double ratio) {
+
+ x = prevFrame.x + (nextFrame.x - prevFrame.x) * ratio;
+ y = prevFrame.y + (nextFrame.y - prevFrame.y) * ratio;
+ z = prevFrame.z + (nextFrame.z - prevFrame.z) * ratio;
+
+ heading = prevFrame.heading +
+ (nextFrame.heading - prevFrame.heading) * ratio;
+ pitch = prevFrame.pitch +
+ (nextFrame.pitch - prevFrame.pitch) * ratio;
+ bank = prevFrame.bank +
+ (nextFrame.bank - prevFrame.bank) * ratio;
+ xScale = prevFrame.xScale +
+ (nextFrame.xScale - prevFrame.xScale) * ratio;
+ yScale = prevFrame.yScale +
+ (nextFrame.yScale - prevFrame.yScale) * ratio;
+ zScale = prevFrame.zScale +
+ (nextFrame.zScale - prevFrame.zScale) * ratio;
+ frameNumber = prevFrame.frameNumber +
+ (nextFrame.frameNumber - prevFrame.frameNumber) * ratio;
+
+ // The following are not interpolated
+ linearValue = prevFrame.linearValue;
+ tension = prevFrame.tension;
+ continuity = prevFrame.continuity;
+ bias = prevFrame.bias;
+ }
+
+ /**
+ * Using hermite interpolation construct a new frame that's
+ * in-between two given frames. We also need to be given a
+ * frame before the first frame and a frame after the second
+ * frame. The calling function will make sure that we get the
+ * four appropriate frames.
+ *
+ * Ratio gives the interpolation value for how far in-between
+ * the new frame should be. (.5 is half-way, etc.)
+ */
+ LwsFrame(LwsFrame prevFrame, LwsFrame frame1,
+ LwsFrame frame2, LwsFrame nextFrame, double u,
+ double adj0, double adj1) {
+
+ double h1, h2, h3, h4;
+ double dd0a, dd0b, ds1a, ds1b;
+
+ // pre-compute spline coefficients
+ double u2, u3, z1;
+ u2 = u * u;
+ u3 = u2 *u;
+ z1 = 3.0f *u2 - u3 - u3;
+ h1 = 1.0f - z1;
+ h2 = z1;
+ h3 = u3 - u2 - u2 + u;
+ h4 = u3 - u2;
+
+ dd0a = (1.0f - frame1.tension) * (1.0f + frame1.continuity)
+ * (1.0f + frame1.bias);
+
+ dd0b = (1.0f - frame1.tension) * (1.0f - frame1.continuity)
+ * (1.0f - frame1.bias);
+
+ ds1a = (1.0f - frame2.tension) * (1.0f - frame2.continuity)
+ * (1.0f + frame2.bias);
+
+ ds1b = (1.0f - frame2.tension) * (1.0f + frame2.continuity)
+ * (1.0f - frame2.bias);
+
+ double[] v = new double[4];
+
+ // interpolate x, y, z
+ v[0] = prevFrame.x; v[1] = frame1.x;
+ v[2] = frame2.x; v[3] = nextFrame.x;
+ x = computeInterpolation (v, dd0a, dd0b, ds1a, ds1b,
+ adj0, adj1, h1, h2, h3, h4);
+ v[0] = prevFrame.y; v[1] = frame1.y;
+ v[2] = frame2.y; v[3] = nextFrame.y;
+ y = computeInterpolation (v, dd0a, dd0b, ds1a, ds1b,
+ adj0, adj1, h1, h2, h3, h4);
+ v[0] = prevFrame.z; v[1] = frame1.z;
+ v[2] = frame2.z; v[3] = nextFrame.z;
+ z = computeInterpolation (v, dd0a, dd0b, ds1a, ds1b,
+ adj0, adj1, h1, h2, h3, h4);
+
+ // interpolate heading pitch and bank
+ v[0] = prevFrame.heading; v[1] = frame1.heading;
+ v[2] = frame2.heading ; v[3] = nextFrame.heading;
+ heading = computeInterpolation (v, dd0a, dd0b, ds1a, ds1b,
+ adj0, adj1, h1, h2, h3, h4);
+
+ v[0] = prevFrame.pitch; v[1] = frame1.pitch;
+ v[2] = frame2.pitch; v[3] = nextFrame.pitch;
+ pitch = computeInterpolation (v, dd0a, dd0b, ds1a, ds1b,
+ adj0, adj1, h1, h2, h3, h4);
+
+ v[0] = prevFrame.bank; v[1] = frame1.bank;
+ v[2] = frame2.bank; v[3] = nextFrame.bank;
+ bank = computeInterpolation (v, dd0a, dd0b, ds1a, ds1b,
+ adj0, adj1, h1, h2, h3, h4);
+
+ // interpolate scale - scale interpolation is assumed to be linear
+ xScale = frame1.xScale + (frame2.xScale - frame1.xScale) * u;
+ yScale = frame1.yScale + (frame2.yScale - frame1.yScale) * u;
+ zScale = frame1.zScale + (frame2.zScale - frame1.zScale) * u;
+
+ // interpolate frame number
+ frameNumber = frame1.frameNumber +
+ (frame2.frameNumber - frame1.frameNumber) * u;
+
+ // The following are not interpolated
+ linearValue = frame2.linearValue;
+
+ // We need to keep the spline smooth between knot points
+ tension = 0.0;
+ continuity = 0.0;
+ bias = 0.0;
+ }
+
+
+ double computeInterpolation(double[] value, double dd0a,
+ double dd0b, double ds1a,
+ double ds1b, double adj0,
+ double adj1, double h1,
+ double h2, double h3, double h4) {
+
+ double dd0, ds1;
+ double delta = value[2] - value[1] ;
+ double result;
+
+ // if adj != 0
+ if (adj0 < -0.0001 || adj0 > 0.0001)
+ dd0 = adj0 * (dd0a * (value[1] - value[0]) + dd0b * delta);
+ else
+ dd0 = 0.5f * (dd0a + dd0b) * delta;
+
+ // if adj != 0
+ if (adj1 < -0.0001 || adj1 > 0.0001)
+ ds1 = adj1 * (ds1a * delta + ds1b * (value[3] - value[2]));
+ else
+ ds1 = 0.5f * (ds1a + ds1b) * delta;
+
+ result = value[1] * h1 + value[2] * h2 + dd0 * h3 + ds1 * h4;
+
+ return (result);
+ }
+
+ double getHeading() {
+ return heading;
+ }
+
+ double getPitch() {
+ return pitch;
+ }
+
+ double getBank() {
+ return bank;
+ }
+
+ /**
+ * Sets the given matrix to contain the position, orientation, and
+ * scale values for the keyframe
+ */
+ void setMatrix(Matrix4d mat) {
+ setRotationMatrix(mat);
+ mat.setTranslation(new Vector3d(x, y, z));
+ Matrix4d m = new Matrix4d();
+ m.setColumn(0, xScale, 0, 0, 0); // setScale not yet implemented
+ m.setColumn(1, 0, yScale, 0, 0);
+ m.setColumn(2, 0, 0, zScale, 0);
+ m.setColumn(3, 0, 0, 0, 1);
+ mat.mul(m);
+ }
+
+ /**
+ * Sets the given matrix to contain the orientation for this keyframe
+ */
+ void setRotationMatrix(Matrix4d mat)
+ {
+ debugOutputLn(TRACE, "setRotMat()");
+ debugOutputLn(VALUES, " p, h, b = " +
+ pitch + ", " +
+ heading + ", " +
+ bank);
+ Matrix4d pitchMat = new Matrix4d();
+ pitchMat.rotX(-pitch);
+ Matrix4d bankMat = new Matrix4d();
+ bankMat.rotZ(bank);
+ mat.rotY(-heading);
+ mat.mul(pitchMat);
+ mat.mul(bankMat);
+ debugOutputLn(VALUES, "setRotMat(), mat = " + mat);
+ }
+
+ Point3f getPosition() {
+ return (new Point3f((float)x, (float)y, (float)z));
+ }
+
+ Point3f getScale() {
+ // Make sure we don't have zero scale components
+ if ((xScale < -0.0001 || xScale > 0.0001) &&
+ (yScale < -0.0001 || yScale > 0.0001) &&
+ (zScale < -0.0001 || zScale > 0.0001)) {
+ return (new Point3f((float)xScale, (float)yScale, (float)zScale));
+ } else {
+ return (new Point3f(1.0f, 1.0f, 1.0f));
+ }
+ }
+
+ double getFrameNum() {
+ return frameNumber;
+ }
+
+ void printVals() {
+ debugOutputLn(VALUES, " x = " + x);
+ debugOutputLn(VALUES, " y = " + y);
+ debugOutputLn(VALUES, " z = " + z);
+ debugOutputLn(VALUES, " xScale = " + xScale);
+ debugOutputLn(VALUES, " yScale = " + yScale);
+ debugOutputLn(VALUES, " zScale = " + zScale);
+ debugOutputLn(VALUES, " heading = " + heading);
+ debugOutputLn(VALUES, " pitch = " + pitch);
+ debugOutputLn(VALUES, " bank = " + bank);
+ debugOutputLn(VALUES, " frameNum = " + frameNumber);
+ debugOutputLn(VALUES, " lin = " + linearValue);
+ debugOutputLn(VALUES, " tension = " + tension);
+ debugOutputLn(VALUES, " continuity = " + continuity);
+ debugOutputLn(VALUES, " bias = " + bias);
+ }
+
+}
+
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsLight.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsLight.java
new file mode 100644
index 0000000..89bd19b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsLight.java
@@ -0,0 +1,269 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import java.io.*;
+import java.util.Vector;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import java.util.Enumeration;
+import com.sun.j3d.loaders.ParsingErrorException;
+
+/**
+ * This class creates a light object from the data in a Scene file. It
+ * instantiates an LwsMotion object to create any associated
+ * animations.
+ */
+
+class LwsLight extends TextfileParser implements LwsPrimitive {
+
+ // data from the file
+ String fileName;
+ String objName;
+ LwsMotion motion;
+ int parent;
+ TransformGroup objectTransform;
+ Vector objectBehavior;
+ Color3f color;
+ int type;
+ Point3f attenuation = new Point3f(1.0f, 0.0f, 0.0f);
+ float spotConeAngle = (float)(Math.PI);
+ // Meta object, used for holding light and
+ LwLightObject lwLight;
+ // light parameters
+ LwsEnvelopeLightIntensity intensityEnvelope = null;
+ Light light = null;
+ final static int DIRECTIONAL = 0, POINT = 1, SPOT = 2;
+
+ /**
+ * Constructor: parses stream and creates data structures for all
+ * light parameters currently handled by the loader
+ */
+ LwsLight(StreamTokenizer st, int totalFrames, float totalTime,
+ int debugVals) throws ParsingErrorException {
+
+ debugPrinter.setValidOutput(debugVals);
+
+ debugOutput(TRACE, "LwsLight()");
+ color = new Color3f(1f, 1f, 1f);
+ lwLight = new LwLightObject(null, 0.0f, null);
+
+ parent = -1;
+ debugOutputLn(LINE_TRACE, "about to get LightName");
+ getAndCheckString(st, "LightName");
+ debugOutputLn(LINE_TRACE, "about to get LightName value");
+ objName = getName(st);
+ debugOutputLn(LINE_TRACE, "got LightName");
+ skip(st, "ShowLight", 2);
+ debugOutputLn(LINE_TRACE, "got ShowLight");
+ getAndCheckString(st, "LightMotion");
+ debugOutputLn(LINE_TRACE, "got LightMotion");
+ motion = new LwsMotion(st, totalFrames, totalTime);
+ debugOutputLn(LINE_TRACE, "got motions");
+
+ // TODO: buggy way to stop processing the light. Should actually
+ // process required/optional fields in order and stop when there's
+ // no more. However, spec says "ShadowCasing" but the files say
+ // "ShadowType".
+
+ while (!isCurrentToken(st, "ShowCamera") &&
+ !isCurrentToken(st, "AddLight")) {
+ // TODO:
+ // Things that we're not yet processing (and should):
+ // - EdgeAngle: for spotlights, this is the angle which
+ // contains the linear falloff toward the edge of the
+ // ConeAngle. This doesn't directly map to J3d's
+ // "concentration" value, so it's left out for now.
+
+ debugOutputLn(LINE_TRACE, "currentToken = " + st.sval);
+
+ if (isCurrentToken(st, "ParentObject")) {
+ parent = (int)getNumber(st);
+ }
+ else if (isCurrentToken(st, "LightColor")) {
+ color.x = (float)getNumber(st)/255f;
+ color.y = (float)getNumber(st)/255f;
+ color.z = (float)getNumber(st)/255f;
+ lwLight.setColor(color);
+ }
+ else if (isCurrentToken(st, "LgtIntensity")) {
+ // TODO: must be able to handle envelopes here
+ String className = getClass().getName();
+ int classIndex = className.lastIndexOf('.');
+ String packageName;
+ if (classIndex < 0)
+ packageName = "";
+ else
+ packageName = className.substring(0, classIndex) + ".";
+ EnvelopeHandler env =
+ new EnvelopeHandler(st, totalFrames, totalTime,
+ packageName + "LwsEnvelopeLightIntensity");
+ if (env.hasValue) {
+ float intensity = (float)env.theValue;
+ color.x *= intensity;
+ color.y *= intensity;
+ color.z *= intensity;
+ lwLight.setIntensity(intensity);
+ }
+ else {
+ intensityEnvelope =
+ (LwsEnvelopeLightIntensity)env.theEnvelope;
+ }
+ }
+ else if (isCurrentToken(st, "LightType")) {
+ type = (int)getNumber(st);
+ }
+ else if (isCurrentToken(st, "Falloff")) {
+ float falloff = (float)getNumber(st);
+ attenuation.y = 1.0f/(1.0f - falloff) - 1.0f;
+ }
+ else if (isCurrentToken(st, "ConeAngle")) {
+ spotConeAngle = (float)getNumber(st) * (float)(Math.PI / 180.0);
+ }
+ try {
+ st.nextToken();
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+ st.pushBack(); // push "ShowCamera" or "AddLight" back on stack
+ }
+
+ int getParent() {
+ return parent;
+ }
+
+ /**
+ * Create Java3D objects from the data we got from the file
+ */
+ void createJava3dObject(int loadBehaviors) {
+ Matrix4d mat = new Matrix4d();
+ mat.setIdentity();
+ // Set the node's transform matrix according to the first frame
+ // of the object's motion
+ LwsFrame firstFrame = motion.getFirstFrame();
+ firstFrame.setMatrix(mat);
+ debugOutputLn(VALUES, "Light transform = " + mat);
+ Transform3D t1 = new Transform3D();
+ t1.set(mat);
+ objectTransform = new TransformGroup(t1);
+ objectTransform.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+ Vector3f defaultDir = new Vector3f(0f, 0f, -1f);
+ Point3f defaultPos = new Point3f(0f, 0f, 0f);
+
+ switch (type) {
+ case DIRECTIONAL:
+ light = new DirectionalLight(color, defaultDir);
+ break;
+ case POINT:
+ light = new PointLight(color, defaultPos, attenuation);
+ break;
+ case SPOT:
+ // Note: spotConeAngle in lw3d is half that of Java3d...
+ light = new SpotLight(color, defaultPos, attenuation, defaultDir,
+ 2 * spotConeAngle, 0.0f);
+ break;
+ default:
+ // Shouldn't get here
+ break;
+ }
+
+ light.setCapability(Light.ALLOW_COLOR_WRITE);
+ if (light != null) {
+ lwLight.setLight(light);
+ BoundingSphere bounds =
+ new BoundingSphere(new Point3d(0.0,0.0,0.0), 100000.0);
+ light.setInfluencingBounds(bounds);
+ objectTransform.addChild(light);
+
+ // load behaviors if we have to
+ objectBehavior = new Vector();
+ if (loadBehaviors != 0) {
+ Behavior b;
+ b = null;
+ motion.createJava3dBehaviors(objectTransform);
+ b = motion.getBehaviors();
+ if (b != null)
+ objectBehavior.addElement(b);
+
+ if (intensityEnvelope != null) {
+ b = null;
+ intensityEnvelope.createJava3dBehaviors(lwLight);
+ b = intensityEnvelope.getBehaviors();
+ if (b != null)
+ objectBehavior.addElement(b);
+ }
+ }
+ }
+ }
+
+ public TransformGroup getObjectNode()
+ {
+ return objectTransform;
+ }
+
+ Light getLight() {
+ return light;
+ }
+
+ public Vector getObjectBehaviors()
+ {
+ debugOutputLn(TRACE, "getObjectBehaviors()");
+ return objectBehavior;
+ }
+
+
+ void printVals()
+ {
+ debugOutputLn(VALUES, " LIGHT vals: ");
+ debugOutputLn(VALUES, " objName = " + objName);
+ motion.printVals();
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsMotion.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsMotion.java
new file mode 100644
index 0000000..472a2f9
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsMotion.java
@@ -0,0 +1,688 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.io.*;
+import java.util.Enumeration;
+import java.util.Vector;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import com.sun.j3d.utils.behaviors.interpolators.*;
+import com.sun.j3d.internal.J3dUtilsI18N;
+import com.sun.j3d.loaders.ParsingErrorException;
+import com.sun.j3d.loaders.IncorrectFormatException;
+
+/**
+ * This class is responsible for parsing the data in a Scene file related to
+ * an object's animation and constructing the appropriate Java3D
+ * Behavior objects. For each keyframe defined for the animation in the
+ * Lightwave file, this class creates a LwsFrame object to parse that
+ * keyframe data and create the appropriate data structures. Then for
+ * each of those LwsFrame objects created, LwsMotion creates a knot
+ * value for a PathInterpolator and fills in the appropriate field. Finally,
+ * the class creates a RotPosScalePathInterpolator with all of the data
+ * from the animation. There are also some utility functions in this
+ * class for dealing with special cases of animations, such as animations
+ * that begin after the first frame of the scene and animations that
+ * define frames in a way that Java3D cannot easily interpret.
+ */
+
+class LwsMotion extends TextfileParser {
+
+ // data from the file
+ String motionName;
+ LwsFrame frames[];
+ int numFrames;
+ int numChannels;
+ boolean loop;
+ float totalTime;
+ int firstFrame;
+ int totalFrames;
+ Behavior behaviors;
+
+ /**
+ * Constructor
+ */
+ LwsMotion(StreamTokenizer st, int frames, float time) {
+ this(st, 0, frames, time, EXCEPTION);
+
+ }
+
+ /**
+ * Constructor: takes tokenizer, 1st frame of this animation, total
+ * number of frames, total time of animation, and the debug settings
+ */
+ LwsMotion(StreamTokenizer st, int firstFrame,
+ int frames, float time, int debugVals)
+ throws ParsingErrorException, IncorrectFormatException {
+
+ debugPrinter.setValidOutput(debugVals);
+ numFrames = 0;
+ totalTime = time;
+ this.firstFrame = firstFrame;
+ totalFrames = frames;
+ debugOutputLn(LINE_TRACE, "about to get motion name");
+ motionName = getName(st);
+ debugOutputLn(LINE_TRACE, "about to get motion");
+ getMotion(st);
+ }
+
+ /**
+ * This method parses the tokenizer and creates the data structures
+ * that hold the data from that file. For each separate keyframe,
+ * this method calls LwsFrame to parse and interpret that data.
+ */
+ void getMotion(StreamTokenizer st)
+ throws ParsingErrorException, IncorrectFormatException
+ {
+ debugOutputLn(TRACE, "getMotion()");
+ numChannels = (int)getNumber(st);
+ if (numChannels != 9) {
+ throw new IncorrectFormatException(
+ J3dUtilsI18N.getString("LwsMotion0"));
+ }
+ debugOutputLn(LINE_TRACE, "got channels");
+
+ numFrames = (int)getNumber(st);
+ frames = new LwsFrame[numFrames];
+ debugOutputLn(VALUES, "got frames" + numFrames);
+
+ for (int i = 0; i < numFrames; ++i) {
+ frames[i] = new LwsFrame(st);
+ }
+
+ debugOutput(LINE_TRACE, "got all frames");
+
+ getAndCheckString(st, "EndBehavior");
+ int repeatVal = (int)getNumber(st);
+ if (repeatVal == 1)
+ loop = false;
+ else
+ loop = true;
+
+ // need to make sure frame info is in sycn with j3d
+ // fixFrames();
+ }
+
+ /**
+ * The previous version of this method looked for sucessive frames with
+ * the same rotation value (mod 2PI). If it found such frames, it would
+ * divide that interval into 4 separate frames.
+ * This fix is not sufficient for various rotation cases, though. For
+ * instance, if the rotation difference between two frames is more than
+ * 2PI, the rotation will not case a flag to be fixed and the resulting
+ * rotation will only be between the delta of the two rotations, mod 2PI.
+ * The real fix should behave as follows:
+ * - Iterate through all sucessive frames
+ * - For any two frames that have rotation components that differ by more
+ * than PI/2 (one quarter rotation - no reason for this, but let's pick a
+ * small value to give our resulting path interpolations a better chance
+ * of behaving correctly), figure out how many frames we need to create to
+ * get increments of <= PI/2 between each frame.
+ * - Create these new frames
+ * - Set the odl frames pointer to the new frames structures.
+ */
+
+ void fixFrames() {
+
+ boolean addedFrames = false;
+ Vector newFramesList = new Vector();
+ double halfPI = (float)(Math.PI/2);
+ LwsFrame finalFrame = null;
+
+ for (int i = 1 ; i < numFrames; ++i) {
+ LwsFrame prevFrame;
+ LwsFrame lastFrame = frames[i-1];
+ LwsFrame thisFrame = frames[i];
+ LwsFrame nextFrame;
+
+ finalFrame = thisFrame;
+ newFramesList.add(lastFrame);
+
+ double largestAngleDifference = 0;
+ double thisAngle = thisFrame.getHeading();
+ double lastAngle = lastFrame.getHeading();
+ double angleDifference = Math.abs(thisAngle - lastAngle);
+ if (angleDifference > largestAngleDifference)
+ largestAngleDifference = angleDifference;
+
+ thisAngle = thisFrame.getPitch();
+ lastAngle = lastFrame.getPitch();
+ angleDifference = Math.abs(thisAngle - lastAngle);
+ if (angleDifference > largestAngleDifference)
+ largestAngleDifference = angleDifference;
+
+ thisAngle = thisFrame.getBank();
+ lastAngle = lastFrame.getBank();
+ angleDifference = Math.abs(thisAngle - lastAngle);
+ if (angleDifference > largestAngleDifference)
+ largestAngleDifference = angleDifference;
+
+ if (largestAngleDifference > halfPI) {
+ // Angles too big - create new frames
+ addedFrames = true;
+ int numNewFrames = (int)(largestAngleDifference/halfPI);
+ double increment = 1.0/(double)(numNewFrames+1);
+ double currentRatio = increment;
+
+ double totalf = frames[numFrames-1].getFrameNum();
+ double tlength = (thisFrame.getFrameNum() -
+ lastFrame.getFrameNum())/totalf;
+ double adj0;
+ double adj1;
+
+ // get the previous and next frames
+ if ((i-1) < 1) {
+ prevFrame = frames[i-1];
+ adj0 = 0.0;
+ } else {
+ prevFrame = frames[i-2];
+ adj0 = tlength/((thisFrame.getFrameNum() -
+ prevFrame.getFrameNum())/totalf);
+ }
+
+ if ((i+1) < numFrames) {
+ nextFrame = frames[i+1];
+ adj1 = tlength/((nextFrame.getFrameNum()-
+ lastFrame.getFrameNum())/totalf);
+ } else {
+ nextFrame = frames[i];
+ adj1 = 1.0;
+ }
+
+ for (int j = 0; j < numNewFrames; ++j) {
+
+ LwsFrame newFrame;
+
+ // if linear interpolation
+ if (thisFrame.linearValue == 1) {
+ newFrame = new LwsFrame(lastFrame,
+ thisFrame, currentRatio);
+
+ // if spline interpolation
+ } else {
+ newFrame = new LwsFrame(prevFrame, lastFrame,
+ thisFrame, nextFrame,
+ currentRatio, adj0, adj1);
+ }
+
+ currentRatio += increment;
+ newFramesList.add(newFrame);
+ }
+ }
+ }
+
+ // Now add in final frame
+ if (finalFrame != null)
+ newFramesList.add(finalFrame);
+ if (addedFrames) {
+
+ // Recreate frames array from newFramesList
+ LwsFrame newFrames[] = new LwsFrame[newFramesList.size()];
+ Enumeration elements = newFramesList.elements();
+ int index = 0;
+ while (elements.hasMoreElements()) {
+ newFrames[index++] = (LwsFrame)elements.nextElement();
+ }
+ frames = newFrames;
+ numFrames = frames.length;
+ for (int i = 0; i < numFrames; ++i) {
+ debugOutputLn(VALUES, "frame " + i + " = " + frames[i]);
+ frames[i].printVals();
+ }
+ }
+ }
+
+ /**
+ * Utility for getting integer mod value
+ */
+ int intMod(int divisee, int divisor) {
+ int tmpDiv = divisee;
+ int tmpDivisor = divisor;
+ if (tmpDiv < 0)
+ tmpDiv = -tmpDiv;
+ if (tmpDivisor < 0)
+ tmpDivisor = -tmpDivisor;
+ while (tmpDiv > tmpDivisor) {
+ tmpDiv -= tmpDivisor;
+ }
+ return tmpDiv;
+ }
+
+ /**
+ * Class that associates a particular frame with its effective frame
+ * number (which accounts for animations that start after frame 1)
+ */
+ class FrameHolder {
+ double frameNumber;
+ LwsFrame frame;
+
+ FrameHolder(LwsFrame theFrame, double number) {
+ frame = theFrame;
+ frameNumber = number;
+ }
+ }
+
+
+ /**
+ * This method was added to account for animations that start after
+ * the first frame (e.g., Juggler.lws starts at frame 30). We need
+ * to alter some of the information for the frames in this "frame subset"
+ */
+ void playWithFrameTimes(Vector framesVector) {
+ debugOutputLn(TRACE, "playWithFrameTimes: firstFrame = " +
+ firstFrame);
+ if (firstFrame == 1) {
+ return;
+ }
+ else if (frames[numFrames-1].getFrameNum() < totalFrames) {
+ // First, create a vector that holds all LwsFrame's in frame
+ // increasing order (where order is started at firstFrame Modulo
+ // this motion's last frame
+ int motionLastFrame =
+ (int)(frames[numFrames-1].getFrameNum() + .4999999);
+ int newFirstFrame = intMod(firstFrame, motionLastFrame);
+ int newLastFrame = intMod(totalFrames, motionLastFrame);
+ int index = 0;
+ while (index < numFrames) {
+ if (frames[index].getFrameNum() >= newFirstFrame)
+ break;
+ ++index;
+ }
+ int startIndex = index;
+ if (frames[startIndex].getFrameNum() > firstFrame &&
+ startIndex > 0)
+ startIndex--; // Actually, should interpolate
+ index = startIndex;
+ if (newFirstFrame < newLastFrame) {
+ while (index < numFrames &&
+ frames[index].getFrameNum() <= newLastFrame) {
+ FrameHolder frameHolder =
+ new FrameHolder(frames[index],
+ frames[index].getFrameNum() -
+ newFirstFrame);
+ framesVector.addElement(frameHolder);
+ ++index;
+ }
+ }
+ else {
+ double currentNewFrameNumber = -1.0;
+ while (index < numFrames) {
+ currentNewFrameNumber = frames[index].getFrameNum() -
+ newFirstFrame;
+ FrameHolder frameHolder =
+ new FrameHolder(frames[index],
+ currentNewFrameNumber);
+ framesVector.addElement(frameHolder);
+ ++index;
+ }
+ index = 0;
+ while (index <= startIndex &&
+ frames[index].getFrameNum() <= newLastFrame) {
+ if (index == 0) {
+ LwsFrame newFrame =
+ new LwsFrame(frames[index],
+ frames[index+1],
+ 1.0/(frames[index+1].getFrameNum() -
+ frames[index].getFrameNum()));
+ FrameHolder frameHolder =
+ new FrameHolder(newFrame,
+ newFrame.getFrameNum() +
+ currentNewFrameNumber);
+ framesVector.addElement(frameHolder);
+ }
+ else {
+ FrameHolder frameHolder =
+ new FrameHolder(frames[index],
+ frames[index].getFrameNum() +
+ currentNewFrameNumber);
+ framesVector.addElement(frameHolder);
+ }
+ ++index;
+ }
+ }
+ }
+ else {
+ int index = 0;
+ while (index < numFrames) {
+ if (frames[index].getFrameNum() >= firstFrame)
+ break;
+ ++index;
+ }
+ int startIndex = index;
+ if (frames[startIndex].getFrameNum() > firstFrame &&
+ startIndex > 0) {
+ // Interpolate to first frame
+ double ratio = (double)firstFrame /
+ (frames[startIndex].getFrameNum() -
+ frames[startIndex-1].getFrameNum());
+ LwsFrame newFrame = new LwsFrame(frames[startIndex-1],
+ frames[startIndex],
+ ratio);
+ FrameHolder frameHolder =
+ new FrameHolder(newFrame, newFrame.getFrameNum() -
+ firstFrame);
+ framesVector.addElement(frameHolder);
+ }
+ index = startIndex;
+ while (index < numFrames &&
+ frames[index].getFrameNum() <= totalFrames) {
+ FrameHolder frameHolder =
+ new FrameHolder(frames[index],
+ frames[index].getFrameNum() -
+ firstFrame);
+ framesVector.addElement(frameHolder);
+ ++index;
+ }
+ if (frames[index-1].getFrameNum() < totalFrames) {
+ // Interpolate to last frame
+ double ratio = (double)(totalFrames -
+ frames[index-1].getFrameNum()) /
+ (frames[index].getFrameNum() -
+ frames[index-1].getFrameNum());
+ LwsFrame newFrame = new LwsFrame(frames[index-1],
+ frames[index],
+ ratio);
+ FrameHolder frameHolder =
+ new FrameHolder(newFrame, totalFrames - firstFrame);
+ framesVector.addElement(frameHolder);
+ }
+ }
+ }
+
+ /**
+ * Normally, we just create j3d behaviors from the frames. But if the
+ * animation's first frame is after frame number one, then we have to
+ * shuffle things around to account for playing/looping on this subset
+ * of the total frames of the animation
+ */
+ void createJava3dBehaviorsForFramesSubset(TransformGroup target) {
+
+ debugOutputLn(TRACE, "createJava3dBehaviorsForFramesSubset");
+ Vector frameHolders = new Vector();
+ playWithFrameTimes(frameHolders);
+ long alphaAtOne = 0;
+
+ // determine looping
+ int loopCount;
+ if (loop)
+ loopCount = -1;
+ else
+ loopCount = 1;
+ loopCount = -1;
+
+ int numFrames = frameHolders.size();
+
+ debugOutputLn(VALUES, "totalTime = " + totalTime);
+ debugOutputLn(VALUES, "loopCount = " + loopCount);
+
+ FrameHolder lastFrameHolder =
+ (FrameHolder)frameHolders.elementAt(frameHolders.size() - 1);
+ LwsFrame lastFrame = lastFrameHolder.frame;
+ float animTime = 1000.0f * totalTime *
+ (float)(lastFrameHolder.frameNumber/(float)(totalFrames -
+ firstFrame));
+ debugOutputLn(VALUES, " anim time: " + animTime);
+ debugOutputLn(VALUES, " totalFrames = " + totalFrames);
+
+ if (!loop)
+ alphaAtOne = (long)(1000.0*totalTime - animTime);
+ Alpha theAlpha =
+ new Alpha(loopCount, Alpha.INCREASING_ENABLE,
+ 0, 0, (long)animTime, 0,
+ alphaAtOne, 0, 0, 0);
+
+ float knots[] = new float[numFrames];
+ Point3f[] positions = new Point3f[numFrames];
+ Quat4f[] quats = new Quat4f[numFrames];
+ Point3f[] scales = new Point3f[numFrames];
+ Transform3D yAxis = new Transform3D();
+ Matrix4d mat = new Matrix4d();
+ KBKeyFrame[] keyFrames = new KBKeyFrame[numFrames];
+
+ for (int i=0; i < numFrames; ++i) {
+
+ FrameHolder frameHolder = (FrameHolder)frameHolders.elementAt(i);
+ LwsFrame frame = frameHolder.frame;
+
+ // copy position
+ positions[i] = frame.getPosition();
+
+ // copy scale
+ // Used to hardcode no-scale: scales[i] = 1.0f, 1.0f, 1.0f;
+ // Note that we can't do non-uniform scaling in the current Path
+ // interpolators. The interpolator just uses the x scale.
+ // getScale makes sure that we don't have any zero scale component
+ scales[i] = frame.getScale();
+
+ // copy rotation information
+ frame.setRotationMatrix(mat);
+ debugOutputLn(VALUES, "LwsMotion::createj3dbeh, mat = " + mat);
+ quats[i] = new Quat4f();
+ quats[i].set(mat);
+ debugOutputLn(VALUES, " and quat = " + quats[i]);
+
+ // calculate knot points from frame numbers
+ if (i == 0)
+ knots[i] = 0.0f;
+ else
+ knots[i] = (float)(frameHolder.frameNumber)/
+ (float)(lastFrameHolder.frameNumber);
+
+ // Create KB key frames
+ keyFrames[i] = new KBKeyFrame(knots[i], frame.linearValue,
+ positions[i],
+ (float)frame.heading,
+ (float)frame.pitch,
+ (float)frame.bank,
+ scales[i],
+ (float)frame.tension,
+ (float)frame.continuity,
+ (float)frame.bias);
+
+ debugOutputLn(VALUES, "pos, knots, quat = " +
+ positions[i] + knots[i] + quats[i]);
+ }
+
+ // Pass the KeyFrames to the interpolator an let it do its thing
+ KBRotPosScaleSplinePathInterpolator b = new
+ KBRotPosScaleSplinePathInterpolator(theAlpha,
+ target,
+ yAxis,
+ keyFrames);
+ if (b != null) {
+ behaviors = b;
+ BoundingSphere bounds =
+ new BoundingSphere(new Point3d(0.0,0.0,0.0), 1000000.0);
+ b.setSchedulingBounds(bounds);
+ target.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+ target.addChild(behaviors);
+ }
+ }
+
+ /**
+ * Create j3d behaviors for the data stored in this animation. This is
+ * done by creating a RotPosScalePathInterpolator object that contains
+ * all of the position, orientation, scale data for each keyframe.
+ */
+ void createJava3dBehaviors(TransformGroup target) {
+
+ if (numFrames <= 1)
+ behaviors = null;
+ else {
+ if (firstFrame > 1) {
+ createJava3dBehaviorsForFramesSubset(target);
+ return;
+ }
+
+ long alphaAtOne = 0;
+
+ // determine looping
+ int loopCount;
+ if (loop)
+ loopCount = -1;
+ else
+ loopCount = 1;
+ loopCount = -1;
+
+ debugOutputLn(VALUES, "totalTime = " + totalTime);
+ debugOutputLn(VALUES, "loopCount = " + loopCount);
+
+ float animTime = 1000.0f * totalTime *
+ (float)(frames[numFrames-1].getFrameNum()/(float)totalFrames);
+
+ debugOutputLn(VALUES, " anim time: " + animTime);
+ debugOutputLn(VALUES, " totalFrames = " + totalFrames);
+ debugOutputLn(VALUES, " lastFrame = " +
+ frames[numFrames-1].getFrameNum());
+
+ if (!loop)
+ alphaAtOne = (long)(1000.0*totalTime - animTime);
+ Alpha theAlpha =
+ new Alpha(loopCount, Alpha.INCREASING_ENABLE,
+ 0, 0, (long)animTime, 0,
+ alphaAtOne, 0, 0, 0);
+
+ float knots[] = new float[numFrames];
+ Point3f[] positions = new Point3f[numFrames];
+ Quat4f[] quats = new Quat4f[numFrames];
+ Point3f[] scales = new Point3f[numFrames];
+ Transform3D yAxis = new Transform3D();
+ Matrix4d mat = new Matrix4d();
+ KBKeyFrame[] keyFrames = new KBKeyFrame[numFrames];
+
+ for (int i=0; i < numFrames; ++i) {
+
+ // copy position
+ positions[i] = frames[i].getPosition();
+
+ // copy scale
+ // Used to hardcode no-scale: scales[i] = 1.0f, 1.0f, 1.0f;
+ // Note that we can't do non-uniform scaling in the current Path
+ // interpolators. The interpolator just uses the x scale.
+ // getScale makes sure that we don't have any 0 scale component
+ scales[i] = frames[i].getScale();
+
+ // copy rotation information
+ frames[i].setRotationMatrix(mat);
+ debugOutputLn(VALUES, "LwsMotion::createj3dbeh, mat = " + mat);
+ quats[i] = new Quat4f();
+ quats[i].set(mat);
+ debugOutputLn(VALUES, " and quat = " + quats[i]);
+
+ // calculate knot points from frame numbers
+ if (i == 0)
+ knots[i] = 0.0f;
+ else
+ knots[i] = (float)(frames[i].getFrameNum())/
+ (float)(frames[numFrames-1].getFrameNum());
+
+ // Create KB key frames
+ keyFrames[i] = new KBKeyFrame(knots[i],frames[i].linearValue,
+ positions[i],
+ (float)frames[i].heading,
+ (float)frames[i].pitch,
+ (float)frames[i].bank,
+ scales[i],
+ (float)frames[i].tension,
+ (float)frames[i].continuity,
+ (float)frames[i].bias);
+
+
+ debugOutputLn(VALUES, "pos, knots, quat = " +
+ positions[i] + knots[i] + quats[i]);
+ }
+
+ // Pass the KeyFrames to the interpolator an let it do its thing
+ KBRotPosScaleSplinePathInterpolator b = new
+ KBRotPosScaleSplinePathInterpolator(theAlpha,
+ target,
+ yAxis,
+ keyFrames);
+ if (b != null) {
+ behaviors = b;
+ BoundingSphere bounds =
+ new BoundingSphere(new Point3d(0.0,0.0,0.0), 1000000.0);
+ b.setSchedulingBounds(bounds);
+ target.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+ target.addChild(behaviors);
+ }
+ }
+
+ }
+
+ /**
+ * Returns the Behavior object created for this animation
+ */
+ Behavior getBehaviors() {
+ return behaviors;
+ }
+
+ /**
+ * Returns the first LwsFrame object (which contains the initial
+ * setup for a given object)
+ */
+ LwsFrame getFirstFrame() {
+ if (numFrames > 0)
+ return frames[0];
+ else
+ return null;
+ }
+
+ /**
+ * Utility function for printing values
+ */
+ void printVals() {
+ debugOutputLn(VALUES, " motionName = " + motionName);
+ debugOutputLn(VALUES, " numChannels = " + numChannels);
+ debugOutputLn(VALUES, " numFrames = " + numFrames);
+ debugOutputLn(VALUES, " loop = " + loop);
+ for (int i = 0; i < numFrames; ++i) {
+ debugOutputLn(VALUES, " FRAME " + i);
+ frames[i].printVals();
+ }
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsObject.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsObject.java
new file mode 100644
index 0000000..9a4d384
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsObject.java
@@ -0,0 +1,570 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+import java.awt.Component;
+import java.io.*;
+import java.util.Vector;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import java.util.Enumeration;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import com.sun.j3d.utils.geometry.ColorCube;
+import com.sun.j3d.loaders.ParsingErrorException;
+import com.sun.j3d.loaders.IncorrectFormatException;
+import java.net.MalformedURLException;
+
+import java.net.*;
+
+/**
+ * An LwsObject is passed a handle to the text file that contains the scene
+ * and is responsible for parsing a particular section of that file that
+ * describes a single object. This section defines the type of object
+ * (either a group or some geometry specified by an Object file) and
+ * some keyframe data that describes the an animation of the
+ * orientation/position/scale of the object. For geometry objects, this
+ * class instantiates a J3dLwoParser object to parse the binary data file.
+ * For the keyframe data, the class instantiates an LwsMotion object to
+ * parse and store that data.
+ */
+
+class LwsObject extends TextfileParser implements LwsPrimitive {
+
+ // data from the file
+ String fileName;
+ String objName;
+ LwsMotion motion;
+ int parent;
+ TransformGroup objectTransform;
+ Vector objectBehavior;
+ Vector shapeList = null;
+ boolean hasPivot = false;
+ TransformGroup pivotTransGroup = null;
+
+ URL urlName;
+ String protocol;
+ int fileType;
+
+ /**
+ * Constructor: parses object section of this scene file and
+ * creates all appropriate data structures to hold the information
+ * @param st StreamTokenizer for scene file
+ * @param loadObject boolean specifying that object is not a lw3d Null
+ * object
+ * @param firstFrame int holding the first frame of the scene's animation
+ * @param totalFrames int holding the total number of frames in the scene
+ * @param totalTime float holding the total time of the animation
+ * @param loader Lw3dLoader loader object that was created by user
+ * @param debugVals in holding current debug flags
+ */
+ LwsObject(StreamTokenizer st, boolean loadObject,
+ int firstFrame, int totalFrames, float totalTime,
+ Lw3dLoader loader, int debugVals)
+ throws java.io.FileNotFoundException,
+ ParsingErrorException {
+ debugPrinter.setValidOutput(debugVals);
+ parent = -1;
+
+ fileType = loader.getFileType();
+
+ try {
+ if (loadObject) {
+ // If this is true, then the object is actually described
+ // in an external geometry file. Get that filename
+ fileName = getString(st);
+ String path = null;
+ switch (loader.getFileType()) {
+ case Lw3dLoader.FILE_TYPE_FILENAME:
+ // No other case is current implemented in this loader
+ path = loader.getBasePath();
+ if (path == null)
+ path = loader.getInternalBasePath();
+ if (path != null) {
+ // It's not sufficient to just use the base path.
+ // Lightwave scene files tend to embed path names
+ // to object files that are only correct if you
+ // start from a certain directory. For example, a
+ // scene file in data/ may point to an object in
+ // data/Objects - but in this case
+ // getInternalBasePath() would be data/, so you'd
+ // look for the object in data/data/Objects...
+ // To attempt to overcome this confusing state of
+ // affairs, let's check path/filename
+ // first, then iterate all the way up the path
+ // until there are no more members of path. This
+ // will ensure that we'll at least pick up data
+ // files if they exist off of one of the parent
+ // directories of wherever the scene file is
+ // stored.
+ // No, I don't really like this solution, but I don't
+ // know of a better general approach for now...
+
+ fileName = getQualifiedFilename(path, fileName);
+ }
+ break;
+ case Lw3dLoader.FILE_TYPE_URL:
+ path = "";
+ URL pathUrl = loader.getBaseUrl();
+ if (pathUrl != null) {
+ path = pathUrl.toString();
+ // store the protocol
+ protocol = pathUrl.getProtocol();
+ }
+ else {
+ path = loader.getInternalBaseUrl();
+ // store the protocol
+ protocol = (new URL(path)).getProtocol();
+ }
+
+ urlName = getQualifiedURL(path, fileName);
+ break;
+ }
+ }
+ else
+ // else the object is a lw3d Null object; essentially a group
+ // which contains other objects
+ objName = getString(st);
+ skip(st, "ShowObject", 2);
+ debugOutputLn(LINE_TRACE,
+ "skipped showobject, about to get objectmotion");
+ getAndCheckString(st, "ObjectMotion");
+ debugOutputLn(LINE_TRACE, "got string " + st.sval);
+ // Create an LwsMotion object to parse the animation data
+ motion = new LwsMotion(st, firstFrame, totalFrames,
+ totalTime, debugVals);
+ debugOutputLn(LINE_TRACE, "got motion");
+ boolean hasParent = false; // keeps bones prim from reassigning par
+
+ // TODO: This isn't the greatest way to stop parsing an object
+ // (keying on the ShowOptions parameter), but it seems to be valid
+ // for the current lw3d format
+ while (!isCurrentToken(st, "ShadowOptions")) {
+ if (!hasParent &&
+ isCurrentToken(st, "ParentObject")) {
+ parent = (int)getNumber(st);
+ hasParent = true;
+ }
+ else if (isCurrentToken(st, "PivotPoint")) {
+ // PivotPoint objects are tricky - they make the object
+ // rotate about this new point instead of the default
+ // So setup transform groups such that this happens
+ // correctly.
+ hasPivot = true;
+ float x = (float)getNumber(st);
+ float y = (float)getNumber(st);
+ float z = (float)getNumber(st);
+ Vector3f pivotPoint = new Vector3f(-x, -y, z);
+ Transform3D pivotTransform = new Transform3D();
+ pivotTransform.set(pivotPoint);
+ pivotTransGroup = new TransformGroup(pivotTransform);
+ pivotTransGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+ }
+
+ else if (isCurrentToken(st, "ObjDissolve")) {
+ // Just handle it for now, don't care about value
+ EnvelopeHandler env =
+ new EnvelopeHandler(st, totalFrames, totalTime);
+ }
+ st.nextToken();
+ }
+ getNumber(st); // skip shadow options parameter
+ debugOutputLn(LINE_TRACE, "done with LwsObject constructor");
+ }
+ catch (MalformedURLException e) {
+ throw new FileNotFoundException(e.getMessage());
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ catch (NumberFormatException e) {
+ throw new ParsingErrorException("Expected a number, got " +
+ e.getMessage());
+ }
+ }
+
+ /**
+ * This method takes the given path and filename and checks whether
+ * that file exists. If not, it chops off the last part of pathname
+ * and recurse to try again. This has the effect of searching all the
+ * way up a given pathname for the existence of the file anywhere
+ * along that path. This is somewhat of a hack to get around the
+ * constraining way that Lightwave uses to define its data object
+ * locations in its scene files.
+ *
+ * If the filename is absolute, it will use the path information from
+ * the filename first, then the path information from the lws file.
+ * If the file can not be found in these locations, the local directory
+ * will be searched.
+ * In addition, it will look for filenames converted to lowercase to
+ * make it easier to use between Windows and Unix.
+ */
+
+ String getQualifiedFilename(String pathname, String filename)
+ throws java.io.FileNotFoundException {
+
+ int index;
+ String pathname2 = "";
+
+ // System.out.println ("pathname:"+pathname+" filename:"+filename);
+
+ // Do we have an absolute filename ?
+ if (filename.indexOf (File.separator) == 0) {
+ if ((index = filename.lastIndexOf (File.separator)) != -1) {
+ pathname2 = filename.substring (0, index+1);
+ filename = filename.substring (index+1);
+ }
+ else {
+ return null; // something out of the ordinary happened
+ }
+ }
+
+ // See if we can find the file
+ // ---------------------------
+
+ // Try pathname from absolute filename
+ try {
+ if (new File(pathname2 + filename).exists()) {
+ return (pathname2 + filename);
+ }
+ }
+ catch (NullPointerException ex) {
+ ex.printStackTrace();
+ }
+ // Try lowercase filename
+ if (new File(pathname2 + filename.toLowerCase()).exists()) {
+ return (pathname2 + filename.toLowerCase());
+ }
+
+ // Try original pathname
+ if (new File(pathname + filename).exists()) {
+ return (pathname + filename);
+ }
+ // Try lowercase filename
+ if (new File(pathname + filename.toLowerCase()).exists()) {
+ return (pathname + filename.toLowerCase());
+ }
+
+ // Finally, let's check the local directory
+ if (new File(filename).exists()) {
+ return (filename);
+ }
+ // Try lowercase filename
+ if (new File(filename.toLowerCase()).exists()) {
+ return (filename.toLowerCase());
+ }
+
+ // Conditions that determine when we give up on the recursive search
+ if ((pathname.equals(File.separator)) ||
+ (pathname == null) ||
+ (pathname.equals(""))) {
+
+ throw new java.io.FileNotFoundException(filename);
+ }
+
+ // Try to find the file in the upper directories
+ // Chop off the last directory from pathname and recurse
+ StringBuffer newPathName = new StringBuffer(128);
+ StringTokenizer st = new StringTokenizer(pathname, File.separator);
+ int tokenCount = st.countTokens() - 1;
+ if (pathname.startsWith(java.io.File.separator))
+ newPathName.append(File.separator);
+ for (int i = 0; i < tokenCount; ++i) {
+ String directory = st.nextToken();
+ newPathName.append(directory);
+ newPathName.append(File.separator);
+ }
+
+ String newPath = newPathName.toString();
+ return getQualifiedFilename(newPath, filename);
+ }
+
+ URL getQualifiedURL(String path, String file)
+ throws MalformedURLException {
+
+ URL url = null;
+
+ // try the path and the file -- this is the lightwave spec
+ try {
+ // create url
+ url = new URL(path + file);
+ // see if file exists
+ url.getContent();
+ // return url if no exception is thrown
+ return url;
+ }
+ catch (IOException e) {
+ // Ignore - try something different
+ }
+
+ // try a couple other options, but trying to open connections is slow,
+ // so don't try as many options as getQualifiedFilename
+
+ // try absolute path
+ try {
+ url = new URL(file);
+ url.getContent();
+ }
+ catch (IOException ex) {
+ // Ignore - try something different
+ }
+
+ // try the absolute path with the protocol
+ try {
+ url = new URL(protocol + ":" + file);
+ url.getContent();
+ return url;
+ }
+ catch (IOException ex) {
+ // Nothing else to try so give up
+ throw new MalformedURLException(path + file);
+ }
+ }
+
+ /**
+ * Returns parent object
+ */
+ int getParent() {
+ return parent;
+ }
+
+ /**
+ * Adds the given child to the transform of this node (its parent).
+ */
+ void addChild(LwsPrimitive child) {
+ debugOutputLn(TRACE, "addChild()");
+ if (objectTransform != null) {
+ debugOutputLn(LINE_TRACE, "objectTransform = " + objectTransform);
+ if (child.getObjectNode() != null) {
+ debugOutputLn(LINE_TRACE, "child has object node");
+ if (hasPivot)
+ pivotTransGroup.addChild(child.getObjectNode());
+ else
+ objectTransform.addChild(child.getObjectNode());
+ }
+/*
+ if (child.getObjectBehaviors() != null) {
+ debugOutputLn(LINE_TRACE, "child has behaviors");
+ Group bg = child.getObjectBehaviors();
+ debugOutputLn(VALUES, " child behaviors = " + bg);
+ // TODO: should remove intermediate group nodes
+ objectBehavior.addChild(bg);
+ }
+*/
+ }
+ }
+
+ /**
+ * Creates Java3d objects from the data stored for this object.
+ * The objects created consist of: A TransformGroup that holds the
+ * transform specified by the first keyframe, a Behavior that acts
+ * on the TransformGroup if there are more than 1 keyframes, and
+ * some geometry (created by J3dLwoParser) from an external geometry
+ * file (if the object wasn't an lw3d Null object (group)).
+ */
+ void createJava3dObject(LwsObject cloneObject, int loadBehaviors)
+ throws IncorrectFormatException, ParsingErrorException,
+ FileNotFoundException
+ {
+ String seqToken = new String("_sequence_");
+ Matrix4d mat = new Matrix4d();
+ mat.setIdentity();
+ // Set the node's transform matrix according to the first frame
+ // of the object's motion
+ LwsFrame firstFrame = motion.getFirstFrame();
+ firstFrame.setMatrix(mat);
+ Transform3D t1 = new Transform3D();
+ t1.set(mat);
+ objectTransform = new TransformGroup(t1);
+ objectTransform.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+
+ // This following bit is a hack and should probably be removed.
+ // It was put in here in order to handle the "Tloop" functionality
+ // of holosketch, which was needed for the 1998 Javaone conference
+ // (the HighNoon demo, in particular). Having the code here, or
+ // using it, means that some object file names are tagged as special
+ // because they contain the phrase "_sequence_", which tells this
+ // object file reader that that object file is, in fact, a
+ // sequence file. It then creates a SequenceReader object to
+ // read that file and create an animation from the objects defined
+ // in that file.
+ // See SequenceReader.java for more information on this.
+ // By the way, a better/fuller implementation of this functionality
+ // would involve investigating a standard Plug-In for Lightwave
+ // that allows the writing out of sequence files from Bones data.
+ // i think it would be better to base any Tloop stuff on that
+ // standard than on some proprietary hack of our own.
+
+ if (fileName != null && fileName.indexOf(seqToken) != -1) { // Tloop
+
+ int index = fileName.indexOf(seqToken);
+ index += seqToken.length();
+ String seqFilename = fileName.substring(index);
+ int endIndex = seqFilename.indexOf(".lwo");
+ if (endIndex != -1)
+ seqFilename = seqFilename.substring(0, endIndex);
+ if ((new File(seqFilename)).exists()) {
+ SequenceReader sr =
+ new SequenceReader(seqFilename,
+ motion.totalTime,
+ (int)motion.totalFrames);
+ sr.printLines();
+ sr.createJava3dObjects(debugPrinter.getValidOutput(),
+ loadBehaviors);
+ Group g = sr.getObjectNode();
+ if (g != null)
+ objectTransform.addChild(g);
+
+ // Sequence reader's getObjectBehaviors creates new Vector
+ objectBehavior = sr.getObjectBehaviors();
+
+ return;
+ }
+ }
+
+ // Okay, now that that hack is out of the way, let's get on with
+ // "normal" Lightwave object files.
+ if (fileName != null || urlName != null) {
+ // If this object refers to an obj file, load it and create
+ // geometry from it.
+ if (cloneObject == null) {
+ debugOutputLn(VALUES,
+ "About to load binary file for " + fileName);
+ // Create a J3dLwoParser object to parse the geometry file
+ // and create the appropriate geometry
+ J3dLwoParser objParser = null;
+ switch (fileType) {
+ case Lw3dLoader.FILE_TYPE_FILENAME:
+ objParser =
+ new J3dLwoParser(fileName,
+ debugPrinter.getValidOutput());
+ break;
+ case Lw3dLoader.FILE_TYPE_URL:
+ objParser = new J3dLwoParser(urlName,
+ debugPrinter.getValidOutput());
+ break;
+ }
+ objParser.createJava3dGeometry();
+ // pivot points change the parent transform
+ if (hasPivot) {
+ objectTransform.addChild(pivotTransGroup);
+ }
+ if (objParser.getJava3dShapeList() != null) {
+ shapeList = objParser.getJava3dShapeList();
+ for (Enumeration e = shapeList.elements() ;
+ e.hasMoreElements() ;) {
+ if (!hasPivot || pivotTransGroup == null)
+ objectTransform.addChild((Shape3D)e.nextElement());
+ else
+ pivotTransGroup.addChild((Shape3D)e.nextElement());
+ }
+ }
+ }
+ else {
+ // Already read that file: Clone original object
+ debugOutputLn(LINE_TRACE, "Cloning shapes");
+ Vector cloneShapeList = cloneObject.getShapeList();
+ for (Enumeration e = cloneShapeList.elements() ;
+ e.hasMoreElements() ;) {
+ debugOutputLn(LINE_TRACE, " shape clone");
+ Shape3D shape = (Shape3D)e.nextElement();
+ Shape3D cloneShape = (Shape3D)shape.cloneTree();
+ objectTransform.addChild(cloneShape);
+ }
+ }
+ }
+
+ // Create j3d behaviors for the object's animation
+ objectBehavior = new Vector();
+ if (loadBehaviors != 0) {
+ motion.createJava3dBehaviors(objectTransform);
+ Behavior b = motion.getBehaviors();
+ if (b != null)
+ objectBehavior.addElement(b);
+ }
+ }
+
+ /**
+ * Return list of Shape3D objects for this object file. This is used
+ * when cloning objects (if the scene file requests the same object file
+ * more than once, that object will be cloned instead of recreated each
+ * time).
+ */
+ Vector getShapeList() {
+ return shapeList;
+ }
+
+ /**
+ * Return the TransformGroup that holds this object file
+ */
+ public TransformGroup getObjectNode() {
+ return objectTransform;
+ }
+
+ /**
+ * Return the Group that holds this object's behaviors. The behaviors
+ * are grouped separately from the geometry so that they can be handled
+ * differently by the parent application.
+ */
+ public Vector getObjectBehaviors()
+ {
+ debugOutputLn(TRACE, "getObjectBehaviors()");
+ return objectBehavior;
+ }
+
+
+ /**
+ * Utiliy function to print some of the object values. Used in
+ * debugging.
+ */
+ void printVals()
+ {
+ debugOutputLn(VALUES, " OBJECT vals: ");
+ debugOutputLn(VALUES, " fileName = " + fileName);
+ debugOutputLn(VALUES, " objName = " + objName);
+ motion.printVals();
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsPrimitive.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsPrimitive.java
new file mode 100644
index 0000000..a0b457d
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsPrimitive.java
@@ -0,0 +1,68 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import java.util.Vector;
+import javax.media.j3d.Group;
+import javax.media.j3d.TransformGroup;
+
+/**
+ * This is an interface which is implemented by LwsObject,
+ * LwsFog, LwsBackground, LwsLight, etc. It provides a generic method
+ * for objects to access some of the common data in these classes.
+ */
+
+interface LwsPrimitive {
+
+ // interface from which other Lws types (Object, Camera, etc.)
+ // inherit methods
+
+ public Vector getObjectBehaviors();
+
+ public TransformGroup getObjectNode();
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/ParserObject.java b/src/classes/share/com/sun/j3d/loaders/lw3d/ParserObject.java
new file mode 100644
index 0000000..1504f22
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/ParserObject.java
@@ -0,0 +1,91 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+/**
+ * This class is a superclass of the binary parsing classes. It provides
+ * some basic debugging utilities.
+ */
+
+class ParserObject {
+
+
+ final static int TRACE = DebugOutput.TRACE, VALUES = DebugOutput.VALUES;
+ final static int MISC = DebugOutput.MISC, LINE_TRACE = DebugOutput.LINE_TRACE;
+ final static int NONE = DebugOutput.NONE, EXCEPTION = DebugOutput.EXCEPTION;
+ final static int TIME = DebugOutput.TIME, WARNING = DebugOutput.WARNING;
+
+ protected DebugOutput debugPrinter;
+
+
+ ParserObject() {
+ debugPrinter = new DebugOutput(EXCEPTION);
+ }
+
+ ParserObject(int debugVals) {
+ this();
+ debugPrinter.setValidOutput(debugVals);
+ }
+
+
+ protected void debugOutputLn(int outputType, String theOutput) {
+ if (theOutput.equals(""))
+ debugPrinter.println(outputType, theOutput);
+ else
+ debugPrinter.println(outputType,
+ getClass().getName() + "::" + theOutput);
+ }
+
+ protected void debugOutput(int outputType, String theOutput) {
+ debugPrinter.print(outputType, theOutput);
+ }
+
+
+
+}
+
+
+
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/SequenceLine.java b/src/classes/share/com/sun/j3d/loaders/lw3d/SequenceLine.java
new file mode 100644
index 0000000..b557474
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/SequenceLine.java
@@ -0,0 +1,269 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.awt.Component;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.StreamTokenizer;
+import java.io.IOException;
+import javax.media.j3d.*;
+import javax.vecmath.Point3d;
+
+import com.sun.j3d.loaders.IncorrectFormatException;
+import com.sun.j3d.loaders.ParsingErrorException;
+import java.io.FileNotFoundException;
+
+/**
+ * This class was created to handle "sequence files", which allow
+ * holosketch-type Tloop sequences to be loaded through the lw3d loader.
+ * The class reads a sequence file line by line and uses SequenceLine to
+ * load the file specified in each line.<BR>
+ * Idea behind the Tloop process:<BR>
+ * Artist creates "tloops" (animations with each keyframe's
+ * geometry saved out explicitly) where the geometries are spaced
+ * one frame apart. Then I can automatically create a SwitchValueInterpolator
+ * based on this spacing. If the number of frames in the sequence is
+ * greater than the number of frames in the tloop, then it will automatically
+ * loop until the end of the sequence.<BR>
+ * Process:<BR>
+ * 1) Artist creates an animation of a null group that has a child with some
+ * special name, such as "bucket_sequence_bucketsequence.txt.lwo", which tells
+ * the lw3d loader that it should look for a sequence file by the name of
+ * bucketsequence.txt. What happens to this object is irrelevant (as far as
+ * the loader is concerned); all animation information is taken from its
+ * parent instead.<BR>
+ * 2) Artist saves out the geometry of the bucket at whatever frames she wants
+ * to. If she's saving a tloop (a sequence of frames), she should save them
+ * under the names <filename>xxx.lwo, where xxx is the 3-digit sequence number
+ * (000, 001, 002, etc.).<BR>
+ * 3) Artist creates the sequence file, which lists all saved geometry files
+ * (except sequences - these can be referred to simply by the first file
+ * (...000.lwo)), along with their associated start/end frames. She also lists
+ * the number of files in the sequence, although this parameter is implied
+ * anyway, through the existence of the sequence files and their naming
+ * convention. Maybe we should trash this guy.<BR>
+ * 4) In the lw3d loader, when LwsObject encounters an object with the
+ * filename "..._sequence_<filename>.lwo", it searches for filename. If
+ * found, it parses the file (using the SequenceReader class) to retrieve
+ * all parameters.<BR>
+ * 5) Each SequenceLine creates a Java3D group containing its objects. This
+ * is either a plain-old-Group (if there is only one object) or a Switch group
+ * with a SwitchValueInterpolator.<BR>
+ * 6) SequenceReader constructs a Switch group and adds all SequenceLine groups
+ * to this new group. It also creates a SwitchPathInterpolator (child of
+ * PathInterolator) that contsructs an Alpha based on the startFrame values of
+ * each SequenceLine. It creates a group and adds the SwitchPathInterpolator
+ * plus any SequenceLine SwitchValueInterpolators to this group.<BR>
+ * 7) LwsObject adds the SequenceReader Switch group to its objectTransform.
+ * It does a getBehaviors() from SequenceReader and adds the result (the
+ * SwitchPathInterpolator group) to its objectBehaviors group.<BR>
+ * 8) Done.
+ */
+
+class SequenceLine {
+
+ int startFrame;
+ int endFrame;
+ String fileName;
+
+ Group geometryGroup = null;
+ Behavior behaviors;
+ int numFrames;
+ float totalTime;
+ int totalFrames;
+
+ // storedRefList keeps references to already loaded objects
+ static Hashtable storedRefList = new Hashtable();
+
+ SequenceLine(StreamTokenizer st, float time, int frames)
+ throws ParsingErrorException {
+ try {
+ totalTime = time;
+ totalFrames = frames;
+ startFrame = (int)st.nval;
+ st.nextToken();
+ endFrame = (int)st.nval;
+ st.nextToken();
+ fileName = st.sval;
+ numFrames = endFrame - startFrame + 1;
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+
+ /**
+ * Creates a SwitchValueInterpolator which is used to switch between
+ * the objects specified for the sequence. This is done for files
+ * that end in "000", meaning that there are a sequence of files
+ * with that same base name that should specify every frame of a
+ * sequence for an object. The Switch node is used to hold all of the
+ * files and the Switch Behavior node is used to activate switching
+ * at the right time and to the right object.
+ */
+ private void createSwitchBehavior(Switch target) {
+
+ int loopCount = -1;
+ float animTime = 1000.0f * totalTime *
+ (float)(target.numChildren())/(float)totalFrames;
+ float startTime = 1000f * totalTime *
+ (float)startFrame/(float)totalFrames;
+ Alpha theAlpha =
+ new Alpha(-1, (long)startTime, 0, (long)animTime, 0, 0);
+
+ SwitchValueInterpolator b=new SwitchValueInterpolator(theAlpha,target);
+ behaviors = b;
+ BoundingSphere bounds =
+ new BoundingSphere(new Point3d(0.0,0.0,0.0), 1000000.0);
+ b.setSchedulingBounds(bounds);
+ target.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+ target.addChild(behaviors);
+
+ }
+
+ /**
+ * Create Java3d objects from the data in the sequence line. This
+ * means that for a tloop file (ends in "000"), we're going to create
+ * the appropriate geometry for each file, put them all in a Switch
+ * node, then create a SwitchValueInterpolator to swap between the
+ * frames of the tloop. If it's not a tloop, then we're just going to
+ * create the geometry for that file.
+ */
+ void createJava3dObjects(int debugVals, int loadBehaviors)
+ throws IncorrectFormatException, FileNotFoundException {
+ if (fileName.indexOf("000") != -1) { // Tloop
+ int index = fileName.indexOf("000");
+ String fileNameBase = fileName.substring(0, index);
+ Switch s = new Switch();
+ s.setCapability(Switch.ALLOW_SWITCH_READ);
+ s.setCapability(Switch.ALLOW_SWITCH_WRITE);
+ String tempFileName = fileName;
+ int fileNum = 0;
+ while ((new File(tempFileName)).exists()) {
+ if (storedRefList.get(tempFileName) != null) {
+ // System.out.println("retrieve stored version of " +
+ // tempFileName);
+ SharedGroup storedGroup =
+ (SharedGroup)storedRefList.get(tempFileName);
+ Link newLink = new Link(storedGroup);
+ s.addChild(newLink);
+ }
+ else {
+ // System.out.println("reading " + tempFileName);
+ J3dLwoParser objParser = new J3dLwoParser(tempFileName,
+ debugVals);
+ objParser.createJava3dGeometry();
+ TransformGroup t = new TransformGroup();
+ SharedGroup newSharedGroup = new SharedGroup();
+ storedRefList.put(tempFileName, newSharedGroup);
+ newSharedGroup.addChild(t);
+ Link newLink = new Link(newSharedGroup);
+ s.addChild(newLink);
+ if (objParser.getJava3dShapeList() != null) {
+ for (Enumeration e =
+ objParser.getJava3dShapeList().elements() ;
+ e.hasMoreElements() ;) {
+ t.addChild((Shape3D)e.nextElement());
+ }
+ }
+ }
+ ++fileNum;
+ String fileNumString = String.valueOf(fileNum);
+ if (fileNum < 10)
+ fileNumString = "00" + fileNumString;
+ else if (fileNum < 100)
+ fileNumString = "0" + fileNumString;
+ tempFileName = fileNameBase + fileNumString + ".lwo";
+ }
+ behaviors = null;
+ if (loadBehaviors != 0) {
+ createSwitchBehavior(s);
+ }
+ geometryGroup = (Group)s;
+ }
+ else {// Not a tloop, just a file
+ geometryGroup = new Group();
+ if (storedRefList.get(fileName) != null) {
+ // System.out.println("getting old ref to " + fileName);
+ SharedGroup storedGroup =
+ (SharedGroup)storedRefList.get(fileName);
+ Link newLink = new Link(storedGroup);
+ geometryGroup.addChild(newLink);
+ }
+ else {
+ // System.out.println("reading " + fileName);
+ J3dLwoParser objParser = new J3dLwoParser(fileName,
+ debugVals);
+ objParser.createJava3dGeometry();
+ TransformGroup t = new TransformGroup();
+ if (objParser.getJava3dShapeList() != null) {
+ for (Enumeration e = objParser.getJava3dShapeList().elements() ;
+ e.hasMoreElements() ;) {
+ t.addChild((Shape3D)e.nextElement());
+ }
+ }
+ SharedGroup newSharedGroup = new SharedGroup();
+ newSharedGroup.addChild(t);
+ Link newLink = new Link(newSharedGroup);
+ geometryGroup.addChild(newLink);
+ storedRefList.put(fileName, newSharedGroup);
+ }
+ }
+ }
+
+ Group getGeometry() {
+ return geometryGroup;
+ }
+
+ Behavior getBehavior() {
+ return behaviors;
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/SequenceReader.java b/src/classes/share/com/sun/j3d/loaders/lw3d/SequenceReader.java
new file mode 100644
index 0000000..0ba239f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/SequenceReader.java
@@ -0,0 +1,172 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.awt.Component;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.StreamTokenizer;
+import java.io.IOException;
+import javax.media.j3d.*;
+import javax.vecmath.Point3d;
+
+import com.sun.j3d.loaders.IncorrectFormatException;
+import com.sun.j3d.loaders.ParsingErrorException;
+import java.io.FileNotFoundException;
+
+/**
+ * This class was created to read a special file format devised for
+ * JavaOne '98 that allowed Tloop functionality inside of Lightwave. It
+ * would be best to find a more standard solution, including using some
+ * plug-in for lw3d that I've heard of that allows artists to automatically
+ * save out the geometry for a file at every frame.
+ */
+
+class SequenceReader {
+
+
+ Vector sequenceLines;
+ float totalTime;
+ int totalFrames;
+
+ TransformGroup objectTransform;
+ Vector behaviorVector;
+
+ /**
+ * Constructor: parses a sequence file and creates a new SequenceLine
+ * object to read in every line of the file
+ */
+ SequenceReader(String filename, float time, int frames)
+ throws ParsingErrorException {
+ totalTime = time;
+ totalFrames = frames;
+ sequenceLines = new Vector();
+ try {
+ // System.out.println("reading sequence from " + filename);
+ StreamTokenizer st = new StreamTokenizer(new BufferedReader(
+ new FileReader(filename)));
+ st.wordChars('_', '_');
+ st.wordChars('/', '/');
+ int type = st.nextToken();
+ while (st.ttype != StreamTokenizer.TT_EOF) {
+ sequenceLines.addElement(new SequenceLine(st,
+ totalTime,
+ totalFrames));
+ st.nextToken();
+ }
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+
+ /**
+ * Creates Java3D objects from the data defined in the sequence
+ * file. Calls each sequenceLine object to create its own
+ * j3d objects, then puts all of those objects in a single Switch
+ * node. Finally, it creates a SwitchPathInterpolator object which
+ * handles switching between each object/s defined by each line
+ */
+ void createJava3dObjects(int debugVals, int loadBehaviors)
+ throws FileNotFoundException {
+
+ objectTransform = new TransformGroup();
+ behaviorVector = new Vector();
+ Enumeration e = sequenceLines.elements();
+ Switch switchNode = new Switch();
+ switchNode.setCapability(Switch.ALLOW_SWITCH_READ);
+ switchNode.setCapability(Switch.ALLOW_SWITCH_WRITE);
+ objectTransform.addChild(switchNode);
+ while (e.hasMoreElements()) {
+ SequenceLine line = (SequenceLine)e.nextElement();
+ line.createJava3dObjects(debugVals, loadBehaviors);
+ if (line.getGeometry() != null)
+ switchNode.addChild(line.getGeometry());
+ //objectTransform.addChild(line.getGeometry());
+ if (line.getBehavior() != null) {
+ behaviorVector.addElement(line.getBehavior());
+ }
+ }
+ float knots[] = new float[sequenceLines.size() + 1];
+ for (int i = 0; i < knots.length-1; ++i) {
+ SequenceLine sl = (SequenceLine)sequenceLines.elementAt(i);
+ knots[i] = (float)sl.startFrame/(float)totalFrames;
+ }
+ knots[knots.length-1] = 1.0f;
+ Alpha theAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE,
+ 0, 0, (long)(1000f * totalTime), 0,
+ 0, 0, 0, 0);
+
+ SwitchPathInterpolator switchPath =
+ new SwitchPathInterpolator(theAlpha,
+ knots,
+ switchNode);
+ BoundingSphere bounds =
+ new BoundingSphere(new Point3d(0.0,0.0,0.0), 1000000.0);
+ switchPath.setSchedulingBounds(bounds);
+ switchNode.addChild(switchPath);
+ behaviorVector.addElement(switchPath);
+ }
+
+ TransformGroup getObjectNode() {
+ return objectTransform;
+ }
+
+ Vector getObjectBehaviors() {
+ return behaviorVector;
+ }
+
+ void printLines() {
+ Enumeration e = sequenceLines.elements();
+ while (e.hasMoreElements()) {
+ SequenceLine line = (SequenceLine)e.nextElement();
+ }
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/ShapeHolder.java b/src/classes/share/com/sun/j3d/loaders/lw3d/ShapeHolder.java
new file mode 100644
index 0000000..38f9d66
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/ShapeHolder.java
@@ -0,0 +1,267 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.util.Vector;
+import javax.vecmath.Vector3f;
+
+
+/**
+ * This class holds all of the vertex/facet/normal/surface-reference
+ * data for a particular object. It has utilities to calculate facet normals,
+ * but this is no longer in use since using the new GeomInfo utilities.
+ */
+
+class ShapeHolder extends ParserObject {
+
+
+ Vector facetSizesList;
+ Vector facetIndicesList;
+ int facetIndicesArray[];
+ int currentNumIndices = 0;
+ int numSurf;
+ int numVerts;
+ int facetIndices[];
+ int facetSizes[];
+ int normalIndices[];
+ float normalCoords[];
+ float coordsArray[];
+
+ ShapeHolder() {
+ }
+
+ ShapeHolder(int debugVals) {
+ super(debugVals);
+ }
+
+
+ /**
+ * Print out (to stdout) the geometry data (coords, indices,
+ * and facet sizes). This is a debugging utility.
+ */
+ void printGeometryData(LwoSurface surface) {
+ int i, j;
+ int indicesIndex = 0;
+ System.out.println("\nPolygon Data:");
+ System.out.println(" Surface color = " + surface.color);
+ System.out.println(" Surface diffuse = " + surface.diffuseColor);
+ for (i = 0; i < facetSizes.length; ++i) {
+ int polySize = facetSizes[i];
+ System.out.println("Facet of size " + polySize);
+ for (j = 0; j < polySize; ++j) {
+ int coordIndex = 3 * facetIndices[indicesIndex++];
+ System.out.println("x, y, z = " +
+ coordsArray[coordIndex] + ", " +
+ coordsArray[coordIndex+1] + ", " +
+ coordsArray[coordIndex+2]);
+ }
+ }
+ }
+
+ /**
+ * Constructs geometry arrays given a winding rule (it turns out that
+ * lw3d winding is opposite of j3d winding, so this is always set to
+ * true in J3dLwoParser)
+ */
+ void createArrays(boolean reverseWinding) {
+ debugOutputLn(TRACE, "createArrays()");
+ // debugOutputLn(VALUES, "facetIndices, faceSizesList = " +
+ // facetIndicesList + facetSizesList);
+ //debugOutputLn(VALUES, "ind and sizes size " +
+ // facetIndicesList.size() + ", " +
+ // facetSizesList.size());
+ //facetIndices =
+ // new int[facetIndicesList.size()];
+ facetIndices = new int[currentNumIndices];
+ if (reverseWinding) {
+ int facetBeginIndex = 0;
+ for (int facetIndex = 0;
+ facetIndex < facetSizesList.size();
+ ++facetIndex) {
+ int currFaceSize =
+ ((Integer)facetSizesList.elementAt(facetIndex)).intValue();
+ int tempFace[] = new int[currFaceSize];
+ for (int j = 0; j < currFaceSize; ++j) {
+ facetIndices[facetBeginIndex + j] =
+ facetIndicesArray[facetBeginIndex +
+ currFaceSize - j - 1];
+ }
+ facetBeginIndex += currFaceSize;
+ }
+
+ }
+ else {
+ for (int i = 0; i < facetIndices.length; ++i) {
+ facetIndices[i] = facetIndicesArray[i];
+ }
+ }
+
+ debugOutputLn(LINE_TRACE, "facetIndices.len and coordsArray.len = " +
+ facetIndices.length + ", " + coordsArray.length);
+ if (((Integer)facetSizesList.elementAt(0)).intValue() < 3) {
+ // if we're dealing with point/line primitives, then let's abandon
+ // the indexed route and simply construct a new coordsArray
+ // that holds the direct values we need for a GeometryArray
+ // object
+ debugOutputLn(LINE_TRACE, "Using direct geometry because " +
+ "facetIndices is of size " +
+ facetIndices.length +
+ " and coordsArray is of length "+
+ coordsArray.length);
+ float newCoordsArray[] = new float[facetIndices.length * 3];
+ int newCoordsIndex = 0;
+ for (int i = 0; i < facetIndices.length; ++i) {
+ newCoordsArray[newCoordsIndex++] =
+ coordsArray[facetIndices[i]*3];
+ newCoordsArray[newCoordsIndex++] =
+ coordsArray[facetIndices[i]*3+1];
+ newCoordsArray[newCoordsIndex++] =
+ coordsArray[facetIndices[i]*3+2];
+ }
+ coordsArray = newCoordsArray;
+ facetIndices = null;
+ }
+
+ facetSizes =
+ new int[facetSizesList.size()];
+ for (int i = 0; i < facetSizes.length; ++i) {
+ facetSizes[i] =
+ ((Integer)facetSizesList.elementAt(i)).intValue();
+ }
+
+ facetSizesList = null; // Force garbage collection on Vectors
+ facetIndicesList = null;
+ facetIndicesArray = null;
+ }
+
+ /**
+ * Force gc on all array objects
+ */
+ void nullify() {
+ facetSizesList = null; // Force garbage collection on everything
+ facetIndicesList = null;
+ facetIndicesArray = null;
+ facetSizes = null;
+ facetIndices = null;
+ normalCoords = null;
+ normalIndices = null;
+ }
+
+ /**
+ * This method calculates facet normals for the geometry. It is no
+ * longer used, as we're now using the GeometryInfo utility to calculate
+ * smooth normals
+ */
+ void calcNormals() {
+ debugOutputLn(TRACE, "calcNormals()");
+ debugOutputLn(LINE_TRACE, "coordsLength, facetsizes.len = " +
+ coordsArray.length + ", " + facetSizes.length);
+ if (facetSizes[0] > 2) {
+ // points and lines don't need normals, polys do
+ if (facetIndices != null) {
+ normalIndices = new int[facetIndices.length];
+ normalCoords = new float[facetIndices.length * 3];
+ }
+ else {
+ normalCoords = new float[coordsArray.length];
+ }
+ debugOutputLn(LINE_TRACE, "normalCoords, incides len = " +
+ normalCoords.length + ", " +
+ ((facetIndices == null) ? 0 : normalIndices.length));
+ int facetIndex = 0;
+ int tempIndex = -1;
+ for (int i = 0; i < facetSizes.length; i += 1) {
+ Vector3f norm;
+ int currFacetSize = facetSizes[i];
+ //debugOutputLn(LINE_TRACE, " i, facetIndex, currSize = " +
+ // i + ", " + facetIndex + ", " + currFacetSize);
+ if (currFacetSize < 3) {
+ // This shouldn't occur
+ norm = new Vector3f(0f, 0f, 1f);
+ }
+ else {
+ Vector3f v1, v2;
+ int index1, index2, index3;
+ if (facetIndices != null) {
+ index1 = facetIndices[facetIndex];
+ index2 = facetIndices[facetIndex+1];
+ index3 = facetIndices[facetIndex+2];
+ //debugOutputLn(VALUES, " index123 = " +
+ // index1 + ", " + index2 + ", " + index3);
+ }
+ else {
+ index1 = facetIndex;
+ index2 = facetIndex+1;
+ index3 = facetIndex+2;
+ }
+ v1 = new
+ Vector3f(coordsArray[index2*3] - coordsArray[index1*3],
+ coordsArray[index2*3+1] - coordsArray[index1*3+1],
+ coordsArray[index2*3+2] - coordsArray[index1*3+2]);
+ v2 = new
+ Vector3f(coordsArray[index3*3] - coordsArray[index1*3],
+ coordsArray[index3*3+1] - coordsArray[index1*3+1],
+ coordsArray[index3*3+2] - coordsArray[index1*3+2]);
+ //debugOutputLn(VALUES, "v1, v2 = " + v1 + v2);
+ norm = new Vector3f();
+ norm.cross(v1, v2);
+ norm.normalize(norm);
+ }
+
+ for (int j = 0; j < currFacetSize; ++j) {
+ int normIndex = facetIndex + j;
+ normalCoords[normIndex*3] = norm.x;
+ normalCoords[normIndex*3+1] = norm.y;
+ normalCoords[normIndex*3+2] = norm.z;
+ if (facetIndices != null)
+ normalIndices[normIndex] = normIndex;
+ }
+ facetIndex += currFacetSize;
+ }
+ }
+ debugOutputLn(TRACE, "done with calcNormals()");
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/SwitchPathInterpolator.java b/src/classes/share/com/sun/j3d/loaders/lw3d/SwitchPathInterpolator.java
new file mode 100644
index 0000000..eb167da
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/SwitchPathInterpolator.java
@@ -0,0 +1,129 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+ package com.sun.j3d.loaders.lw3d;
+
+
+import javax.vecmath.*;
+import java.util.BitSet;
+import java.util.Enumeration;
+
+import javax.media.j3d.Alpha;
+import javax.media.j3d.Node;
+import javax.media.j3d.NodeReferenceTable;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Switch;
+import com.sun.j3d.internal.J3dUtilsI18N;
+
+/**
+ * This class was used in conjunction with SequenceReader to create
+ * Tloop functionality inside of Lightwave files. This behavior handles
+ * the switching between objects defined in separate lines of a
+ * sequence file. That is, each line in a sequence file has the name
+ * of an object (or an object sequence, if the name ends in "000")
+ * and details the start and end frames that that object should be active.
+ * This class determines which object/s defined in the file should be active
+ * at any given time during the animation.
+ */
+
+class SwitchPathInterpolator extends FloatValueInterpolator {
+
+ Switch target;
+ int firstSwitchIndex;
+ int lastSwitchIndex;
+ int currentChild;
+ int childCount;
+
+ /**
+ * Constructs a new SwitchPathInterpolator object.
+ * @param alpha the alpha object for this interpolator
+ * @param knots an array of knot values that specify a spline
+ */
+ SwitchPathInterpolator(Alpha alpha, float knots[], Switch target) {
+
+ super(alpha, knots, new float[knots.length]);
+
+ if (knots.length != (target.numChildren() + 1))
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("SwitchPathInterpolator0"));
+
+ this.target = target;
+ firstSwitchIndex = 0;
+ lastSwitchIndex = target.numChildren() - 1;
+ childCount = lastSwitchIndex + 1;
+ }
+
+ /**
+ * This method sets the correct child for the Switch node according
+ * to alpha
+ * @param criteria enumeration of criteria that have triggered this wakeup
+ */
+
+ public void processStimulus(Enumeration criteria) {
+
+ int child;
+
+ // Handle stimulus
+ if (this.getAlpha() != null) {
+
+ // Let PathInterpolator calculate the correct
+ // interpolated knot point
+ computePathInterpolation();
+
+ if (currentKnotIndex > 0)
+ child = currentKnotIndex - 1;
+ else
+ child = 0;
+
+ if (target.getWhichChild() != child) {
+ target.setWhichChild(child);
+ }
+
+ if ((this.getAlpha()).finished())
+ return;
+ }
+
+ wakeupOn(defaultWakeupCriterion);
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/TargaReader.java b/src/classes/share/com/sun/j3d/loaders/lw3d/TargaReader.java
new file mode 100644
index 0000000..a96acad
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/TargaReader.java
@@ -0,0 +1,199 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.applet.Applet;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.MediaTracker;
+import java.awt.Frame;
+import java.awt.Image;
+import java.awt.image.MemoryImageSource;
+import java.awt.Toolkit;
+import java.io.FileNotFoundException;
+import java.io.DataInputStream;
+import java.io.FileInputStream;
+import java.io.BufferedInputStream;
+import com.sun.j3d.loaders.IncorrectFormatException;
+import com.sun.j3d.loaders.ParsingErrorException;
+import java.io.IOException;
+
+/**
+ * This class parses a standard Targa file and retrieves the image stored
+ * therein, storing the pixel data in a BufferedImage.
+ */
+
+class TargaReader extends ParserObject {
+
+ BufferedInputStream bufferedReader;
+ Image theImage = null;
+
+ /**
+ * Constructor: creates file reader and calls parseFile() to do the real
+ * work
+ */
+ TargaReader(String fileName, int debugVals) throws FileNotFoundException {
+ super(debugVals);
+ debugOutputLn(TRACE, "constructor");
+ bufferedReader = new BufferedInputStream(
+ new DataInputStream(new FileInputStream(fileName)));
+ if (bufferedReader != null)
+ parseFile();
+ }
+
+ /**
+ * Returns the image that was created from parsing the targa file (null
+ * if the file reading failed)
+ */
+ Image getImage() {
+ return theImage;
+ }
+
+ /**
+ * This method parses the file and stores the pixel data in a
+ * BufferedImage. The basic file format is:
+ * Byte Description
+ *
+ * 0 Image ID Length
+ * 1 Colormap type
+ * 2 Image Type
+ * 3-4 Colormap spec: 1st entry index
+ * 5-6 Colormap spec: length
+ * 7 Colormap spec: entry size
+ * 8-9 X-origin of lower-left corner
+ * 10-11 Y-origin of lower-left corner
+ * 12-13 Image width
+ * 14-15 Image height
+ * 16 Pixel depth
+ * 17 00(origin)(alpha)
+ * first 2 bytes 0, next 2 starting corner,
+ * last four number of overlay bits per pixel
+ * 18- Image ID
+ * ?? Colormap data
+ * ?? Image Data
+ * ?? Developer Area
+ * ?? Extension Area
+ * ?? File Footer
+ *
+ * We're going to make some assumptions about the format of files we're
+ * asked to load. In particular, we're not going to do any colormpa-based
+ * images: the images need to be either 24-bit or 32-bit true color.
+ * We're also going to ignore vaiours parameters in the header block, since
+ * they complicate life and don't appear to be used in Lightwave image
+ * files. In particular, the following fields will be ignored:
+ * Image ID, colormap info, xy origins, and alpha/overlay bits.
+ */
+
+ void parseFile()
+ throws IncorrectFormatException, ParsingErrorException {
+ try {
+ int idLength = bufferedReader.read();
+ int colormapPresent = bufferedReader.read();
+ int imageType = bufferedReader.read();
+ bufferedReader.skip(9); // skipping camp and xy origin data
+ int width = bufferedReader.read() | bufferedReader.read() << 8;
+ int height = bufferedReader.read() | bufferedReader.read() << 8;
+ int depth = bufferedReader.read();
+ int flags = bufferedReader.read();
+ boolean bottomToTop = ((flags & 0x20) == 0);
+ boolean leftToRight = ((flags & 0x10) == 0);
+ bufferedReader.skip(idLength);
+
+ // Check on the file parameters to see whether we should punt
+ if ((colormapPresent == 1) ||
+ imageType != 2 ||
+ (depth != 24 &&
+ depth != 32)) {
+ // Punt
+ throw new IncorrectFormatException(
+ "This format is not readable by the Lightwave " +
+ "loader. Only 24- or 32-bit true-color " +
+ "uncompressed Targa images will work");
+ }
+
+ // Image format must be okay for us to read
+ BufferedImage bImage =
+ new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ int[] imageBits =
+ ((DataBufferInt)bImage.getRaster().getDataBuffer()).getData();
+
+ int row;
+ int column;
+
+ for (int i = 0; i < height; ++i) {
+ if (bottomToTop)
+ row = (height - i - 1);
+ else
+ row = i;
+ for (int j = 0; j < width; ++j) {
+
+ if (leftToRight)
+ column = j;
+ else
+ column = (width - j - 1);
+
+ int blue = bufferedReader.read();
+ int green = bufferedReader.read();
+ int red = bufferedReader.read();
+ int alpha = 0xff;
+ if (depth == 32)
+ alpha = bufferedReader.read();
+ imageBits[row*width + column] = alpha << 24 |
+ red << 16 |
+ green << 8 |
+ blue;
+ }
+ }
+ theImage = bImage;
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/TextfileParser.java b/src/classes/share/com/sun/j3d/loaders/lw3d/TextfileParser.java
new file mode 100644
index 0000000..13f9611
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/TextfileParser.java
@@ -0,0 +1,245 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.io.*;
+import com.sun.j3d.loaders.ParsingErrorException;
+
+/**
+ * This class is a superclass for most of the Lws* Scene-file parsing
+ * classes. It provides some debugging utilities, as well as utilities for
+ * reading the types of data common to this loader.
+ */
+
+class TextfileParser {
+
+ // class variables
+ static int WORD = StreamTokenizer.TT_WORD;
+ static int NUMBER = StreamTokenizer.TT_NUMBER;
+ int currentLevel = 3;
+ final static int TRACE = DebugOutput.TRACE, VALUES = DebugOutput.VALUES;
+ final static int MISC = DebugOutput.MISC, LINE_TRACE = DebugOutput.LINE_TRACE;
+ final static int NONE = DebugOutput.NONE, EXCEPTION = DebugOutput.EXCEPTION;
+ final static int TIME = DebugOutput.TIME;
+ protected DebugOutput debugPrinter;
+ char lineSeparatorChar = 0;
+
+ TextfileParser() {
+ debugPrinter = new DebugOutput(EXCEPTION);
+ String lineSeparator = System.getProperty("line.separator");
+ lineSeparatorChar = lineSeparator.charAt(0);
+ debugOutputLn(VALUES, "lineSeparatorChar = " + (int)lineSeparatorChar);
+ }
+
+
+ protected void debugOutputLn(int outputType, String theOutput) {
+ if (theOutput.equals(""))
+ debugPrinter.println(outputType, theOutput);
+ else {
+ debugPrinter.println(outputType,
+ getClass().getName() + "::" + theOutput);
+ }
+ }
+
+ protected void debugOutput(int outputType, String theOutput) {
+ debugPrinter.print(outputType, theOutput);
+ }
+
+ /**
+ * Utility method to advance the tokenizer until we see the given
+ * string. This is used to skip by various parameters that we
+ * currently ignore in the loader.
+ */
+ void skipUntilString(StreamTokenizer st, String theString)
+ throws ParsingErrorException {
+ boolean done = false;
+ try {
+ while (!done) {
+ st.nextToken();
+ if (st.ttype == WORD &&
+ st.sval.equals(theString))
+ done = true;
+ }
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+
+
+ /**
+ * Returns number from the tokenizer. Note that we don't recognize
+ * numbers in the tokenizer automatically because numbers might be in
+ * scientific notation, which isn't processed correctly by
+ * StreamTokenizer
+ */
+ double getNumber(StreamTokenizer st)
+ throws ParsingErrorException, NumberFormatException {
+ try {
+ int token = st.nextToken();
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ checkType(st, WORD);
+ return ((Double.valueOf(st.sval)).doubleValue());
+ }
+
+ /**
+ * Returns String from the tokenizer
+ */
+ String getString(StreamTokenizer st) throws ParsingErrorException {
+ try {
+ st.nextToken();
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ checkType(st, WORD);
+ return (st.sval);
+ }
+
+ /**
+ * Returns a "name" from the stream. This is different from simply a
+ * String because the name could contain whitespace characters
+ * (such as "object 1" or "objectname (sequence)") that would confuse
+ * the string parser. So we just grab all characters until EOL and
+ * concatenate them together to form the name
+ */
+ String getName(StreamTokenizer st) throws ParsingErrorException {
+ String theName = "";
+ st.ordinaryChar(lineSeparatorChar);
+ st.ordinaryChar('\n');
+ st.ordinaryChar('\r');
+ try {
+ st.nextToken();
+ while (st.ttype != lineSeparatorChar &&
+ st.ttype != '\r' &&
+ st.ttype != '\n') {
+ if (st.ttype != '(' &&
+ st.ttype != ')')
+ theName += st.sval;
+ st.nextToken();
+ }
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ st.whitespaceChars(lineSeparatorChar, lineSeparatorChar);
+ st.whitespaceChars('\n', '\n');
+ st.whitespaceChars('\r', '\r');
+ debugOutputLn(VALUES, "name = " + theName);
+ return theName;
+ }
+
+ /**
+ * Gets the next token and ensures that it is the string we were
+ * expecting to see
+ */
+ void getAndCheckString(StreamTokenizer st, String expectedValue)
+ throws ParsingErrorException {
+ try {
+ st.nextToken();
+ checkString(st, expectedValue);
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+
+ /**
+ * Error checking routine - makes sure the current token is the string
+ * we were expecting
+ */
+ void checkString(StreamTokenizer st, String theString) throws
+ ParsingErrorException {
+ if (!(st.ttype == StreamTokenizer.TT_WORD) ||
+ !st.sval.equals(theString))
+ throw new ParsingErrorException(
+ "Bad String Token (wanted " + theString + ", got " + st.sval +
+ ": " + st.toString());
+ }
+
+ /**
+ * Error checking routine - makes sure the current token is of the right
+ * type
+ */
+ void checkType(StreamTokenizer st, int theType)
+ throws ParsingErrorException {
+ if (!(st.ttype == theType))
+ throw new ParsingErrorException(
+ "Bad Type Token, Expected " + theType + " and received" +
+ st.ttype);
+ }
+
+ /**
+ * Utility routine - gets next token, checks it against our expectation,
+ * then skips a given number of tokens. This can be used to parse
+ * through (and ignore) certain parameter/value sets in the files
+ */
+ void skip(StreamTokenizer st, String tokenString, int skipVals)
+ throws ParsingErrorException {
+ try {
+ st.nextToken();
+ checkString(st, tokenString);
+ for (int i = 0; i < skipVals; ++i) {
+ st.nextToken();
+ }
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+
+ /**
+ * Utility method- used to check whether the current token is equal
+ * to the given string
+ */
+ boolean isCurrentToken(StreamTokenizer st, String tokenString) {
+ if (st.ttype == WORD)
+ return (st.sval.equals(tokenString));
+ return false;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/objectfile/DefaultMaterials.java b/src/classes/share/com/sun/j3d/loaders/objectfile/DefaultMaterials.java
new file mode 100644
index 0000000..b573272
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/objectfile/DefaultMaterials.java
@@ -0,0 +1,952 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.objectfile;
+
+/**
+ * This class provides default materials for the object file loader
+ */
+class DefaultMaterials {
+ /**
+ * String that describes the default materials.
+ */
+ static final String materials =
+ "newmtl amber\n" +
+ "Ka 0.0531 0.0531 0.0531\n" +
+ "Kd 0.5755 0.2678 0.0000\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl amber_trans\n" +
+ "Ka 0.0531 0.0531 0.0531\n" +
+ "Kd 0.5755 0.2678 0.0000\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "d 0.8400\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl charcoal\n" +
+ "Ka 0.0082 0.0082 0.0082\n" +
+ "Kd 0.0041 0.0041 0.0041\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl lavendar\n" +
+ "Ka 0.1281 0.0857 0.2122\n" +
+ "Kd 0.2187 0.0906 0.3469\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl navy_blue\n" +
+ "Ka 0.0000 0.0000 0.0490\n" +
+ "Kd 0.0000 0.0000 0.0531\n" +
+ "Ks 0.1878 0.1878 0.1878\n" +
+ "illum 2\n" +
+ "Ns 91.4700\n" +
+ "\n" +
+ "newmtl pale_green\n" +
+ "Ka 0.0444 0.0898 0.0447\n" +
+ "Kd 0.0712 0.3796 0.0490\n" +
+ "Ks 0.1878 0.1878 0.1878\n" +
+ "illum 2\n" +
+ "Ns 91.4700\n" +
+ "\n" +
+ "newmtl pale_pink\n" +
+ "Ka 0.0898 0.0444 0.0444\n" +
+ "Kd 0.6531 0.2053 0.4160\n" +
+ "Ks 0.1878 0.1878 0.1878\n" +
+ "illum 2\n" +
+ "Ns 91.4700\n" +
+ "\n" +
+ "newmtl pale_yellow\n" +
+ "Ka 0.3606 0.3755 0.0935\n" +
+ "Kd 0.6898 0.6211 0.1999\n" +
+ "Ks 0.1878 0.1878 0.1878\n" +
+ "illum 2\n" +
+ "Ns 91.4700\n" +
+ "\n" +
+ "newmtl peach\n" +
+ "Ka 0.3143 0.1187 0.0167\n" +
+ "Kd 0.6367 0.1829 0.0156\n" +
+ "Ks 0.1878 0.1878 0.1878\n" +
+ "illum 2\n" +
+ "Ns 91.4700\n" +
+ "\n" +
+ "newmtl periwinkle\n" +
+ "Ka 0.0000 0.0000 0.1184\n" +
+ "Kd 0.0000 0.0396 0.8286\n" +
+ "Ks 0.1878 0.1878 0.1878\n" +
+ "illum 2\n" +
+ "Ns 91.4700\n" +
+ "\n" +
+ "newmtl redwood\n" +
+ "Ka 0.0204 0.0027 0.0000\n" +
+ "Kd 0.2571 0.0330 0.0000\n" +
+ "Ks 0.1878 0.1878 0.1878\n" +
+ "illum 2\n" +
+ "Ns 91.4700\n" +
+ "\n" +
+ "newmtl smoked_glass\n" +
+ "Ka 0.0000 0.0000 0.0000\n" +
+ "Kd 0.0041 0.0041 0.0041\n" +
+ "Ks 0.1878 0.1878 0.1878\n" +
+ "illum 2\n" +
+ "d 0.9800\n" +
+ "Ns 91.4700\n" +
+ "\n" +
+ "newmtl aqua_filter\n" +
+ "Ka 0.0000 0.0000 0.0000\n" +
+ "Kd 0.3743 0.6694 0.5791\n" +
+ "Ks 0.1878 0.1878 0.1878\n" +
+ "illum 2\n" +
+ "d 0.9800\n" +
+ "Ns 91.4700\n" +
+ "\n" +
+ "newmtl yellow_green\n" +
+ "Ka 0.0000 0.0000 0.0000\n" +
+ "Kd 0.1875 0.4082 0.0017\n" +
+ "Ks 0.1878 0.1878 0.1878\n" +
+ "illum 2\n" +
+ "Ns 91.4700\n" +
+ "\n" +
+ "newmtl bluetint\n" +
+ "Ka 0.1100 0.4238 0.5388\n" +
+ "Kd 0.0468 0.7115 0.9551\n" +
+ "Ks 0.3184 0.3184 0.3184\n" +
+ "illum 9\n" +
+ "d 0.5700\n" +
+ "Ns 60.0000\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl plasma\n" +
+ "Ka 0.4082 0.0816 0.2129\n" +
+ "Kd 1.0000 0.0776 0.4478\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 9\n" +
+ "d 0.7500\n" +
+ "Ns 60.0000\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl emerald\n" +
+ "Ka 0.0470 1.0000 0.0000\n" +
+ "Kd 0.0470 1.0000 0.0000\n" +
+ "Ks 0.2000 0.2000 0.2000\n" +
+ "illum 9\n" +
+ "d 0.7500\n" +
+ "Ns 60.0000\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl ruby\n" +
+ "Ka 1.0000 0.0000 0.0000\n" +
+ "Kd 1.0000 0.0000 0.0000\n" +
+ "Ks 0.2000 0.2000 0.2000\n" +
+ "illum 9\n" +
+ "d 0.7500\n" +
+ "Ns 60.0000\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl sapphire\n" +
+ "Ka 0.0235 0.0000 1.0000\n" +
+ "Kd 0.0235 0.0000 1.0000\n" +
+ "Ks 0.2000 0.2000 0.2000\n" +
+ "illum 9\n" +
+ "d 0.7500\n" +
+ "Ns 60.0000\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl white\n" +
+ "Ka 0.4000 0.4000 0.4000\n" +
+ "Kd 1.0000 1.0000 1.0000\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl red\n" +
+ "Ka 0.4449 0.0000 0.0000\n" +
+ "Kd 0.7714 0.0000 0.0000\n" +
+ "Ks 0.8857 0.0000 0.0000\n" +
+ "illum 2\n" +
+ "Ns 136.4300\n" +
+ "\n" +
+ "newmtl blue_pure\n" +
+ "Ka 0.0000 0.0000 0.5000\n" +
+ "Kd 0.0000 0.0000 1.0000\n" +
+ "Ks 0.0000 0.0000 0.5000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl lime\n" +
+ "Ka 0.0000 0.5000 0.0000\n" +
+ "Kd 0.0000 1.0000 0.0000\n" +
+ "Ks 0.0000 0.5000 0.0000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl green\n" +
+ "Ka 0.0000 0.2500 0.0000\n" +
+ "Kd 0.0000 0.2500 0.0000\n" +
+ "Ks 0.0000 0.2500 0.0000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl yellow\n" +
+ "Ka 1.0000 0.6667 0.0000\n" +
+ "Kd 1.0000 0.6667 0.0000\n" +
+ "Ks 1.0000 0.6667 0.0000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl purple\n" +
+ "Ka 0.5000 0.0000 1.0000\n" +
+ "Kd 0.5000 0.0000 1.0000\n" +
+ "Ks 0.5000 0.0000 1.0000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl orange\n" +
+ "Ka 1.0000 0.1667 0.0000\n" +
+ "Kd 1.0000 0.1667 0.0000\n" +
+ "Ks 1.0000 0.1667 0.0000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl grey\n" +
+ "Ka 0.5000 0.5000 0.5000\n" +
+ "Kd 0.1837 0.1837 0.1837\n" +
+ "Ks 0.5000 0.5000 0.5000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl rubber\n" +
+ "Ka 0.0000 0.0000 0.0000\n" +
+ "Kd 0.0100 0.0100 0.0100\n" +
+ "Ks 0.1000 0.1000 0.1000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl flaqua\n" +
+ "Ka 0.0000 0.4000 0.4000\n" +
+ "Kd 0.0000 0.5000 0.5000\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flblack\n" +
+ "Ka 0.0000 0.0000 0.0000\n" +
+ "Kd 0.0041 0.0041 0.0041\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flblue_pure\n" +
+ "Ka 0.0000 0.0000 0.5592\n" +
+ "Kd 0.0000 0.0000 0.7102\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flgrey\n" +
+ "Ka 0.2163 0.2163 0.2163\n" +
+ "Kd 0.5000 0.5000 0.5000\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl fllime\n" +
+ "Ka 0.0000 0.3673 0.0000\n" +
+ "Kd 0.0000 1.0000 0.0000\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl florange\n" +
+ "Ka 0.6857 0.1143 0.0000\n" +
+ "Kd 1.0000 0.1667 0.0000\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flpurple\n" +
+ "Ka 0.2368 0.0000 0.4735\n" +
+ "Kd 0.3755 0.0000 0.7510\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flred\n" +
+ "Ka 0.4000 0.0000 0.0000\n" +
+ "Kd 1.0000 0.0000 0.0000\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flyellow\n" +
+ "Ka 0.7388 0.4925 0.0000\n" +
+ "Kd 1.0000 0.6667 0.0000\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl pink\n" +
+ "Ka 0.9469 0.0078 0.2845\n" +
+ "Kd 0.9878 0.1695 0.6702\n" +
+ "Ks 0.7429 0.2972 0.2972\n" +
+ "illum 2\n" +
+ "Ns 106.2000\n" +
+ "\n" +
+ "newmtl flbrown\n" +
+ "Ka 0.0571 0.0066 0.0011\n" +
+ "Kd 0.1102 0.0120 0.0013\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl brown\n" +
+ "Ka 0.1020 0.0185 0.0013\n" +
+ "Kd 0.0857 0.0147 0.0000\n" +
+ "Ks 0.1633 0.0240 0.0000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl glass\n" +
+ "Ka 1.0000 1.0000 1.0000\n" +
+ "Kd 0.4873 0.4919 0.5306\n" +
+ "Ks 0.6406 0.6939 0.9020\n" +
+ "illum 2\n" +
+ "Ns 200.0000\n" +
+ "\n" +
+ "newmtl flesh\n" +
+ "Ka 0.4612 0.3638 0.2993\n" +
+ "Kd 0.5265 0.4127 0.3374\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl aqua\n" +
+ "Ka 0.0000 0.4000 0.4000\n" +
+ "Kd 0.0000 0.5000 0.5000\n" +
+ "Ks 0.5673 0.5673 0.5673\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl black\n" +
+ "Ka 0.0000 0.0000 0.0000\n" +
+ "Kd 0.0020 0.0020 0.0020\n" +
+ "Ks 0.5184 0.5184 0.5184\n" +
+ "illum 2\n" +
+ "Ns 157.3600\n" +
+ "\n" +
+ "newmtl silver\n" +
+ "Ka 0.9551 0.9551 0.9551\n" +
+ "Kd 0.6163 0.6163 0.6163\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl dkblue_pure\n" +
+ "Ka 0.0000 0.0000 0.0449\n" +
+ "Kd 0.0000 0.0000 0.1347\n" +
+ "Ks 0.0000 0.0000 0.5673\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl fldkblue_pure\n" +
+ "Ka 0.0000 0.0000 0.0449\n" +
+ "Kd 0.0000 0.0000 0.1347\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl dkgreen\n" +
+ "Ka 0.0000 0.0122 0.0000\n" +
+ "Kd 0.0058 0.0245 0.0000\n" +
+ "Ks 0.0000 0.0490 0.0000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl dkgrey\n" +
+ "Ka 0.0490 0.0490 0.0490\n" +
+ "Kd 0.0490 0.0490 0.0490\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl ltbrown\n" +
+ "Ka 0.1306 0.0538 0.0250\n" +
+ "Kd 0.2776 0.1143 0.0531\n" +
+ "Ks 0.3000 0.1235 0.0574\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl fldkgreen\n" +
+ "Ka 0.0000 0.0122 0.0000\n" +
+ "Kd 0.0058 0.0245 0.0000\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flltbrown\n" +
+ "Ka 0.1306 0.0538 0.0250\n" +
+ "Kd 0.2776 0.1143 0.0531\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl tan\n" +
+ "Ka 0.4000 0.3121 0.1202\n" +
+ "Kd 0.6612 0.5221 0.2186\n" +
+ "Ks 0.5020 0.4118 0.2152\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl fltan\n" +
+ "Ka 0.4000 0.3121 0.1202\n" +
+ "Kd 0.6612 0.4567 0.1295\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl brzskin\n" +
+ "Ka 0.4408 0.2694 0.1592\n" +
+ "Kd 0.3796 0.2898 0.2122\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 25.0000\n" +
+ "\n" +
+ "newmtl lips\n" +
+ "Ka 0.4408 0.2694 0.1592\n" +
+ "Kd 0.9265 0.2612 0.2898\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 25.0000\n" +
+ "\n" +
+ "newmtl redorange\n" +
+ "Ka 0.3918 0.0576 0.0000\n" +
+ "Kd 0.7551 0.0185 0.0000\n" +
+ "Ks 0.4694 0.3224 0.1667\n" +
+ "illum 2\n" +
+ "Ns 132.5600\n" +
+ "\n" +
+ "newmtl blutan\n" +
+ "Ka 0.4408 0.2694 0.1592\n" +
+ "Kd 0.0776 0.2571 0.2041\n" +
+ "Ks 0.1467 0.1469 0.0965\n" +
+ "illum 2\n" +
+ "Ns 25.0000\n" +
+ "\n" +
+ "newmtl bluteal\n" +
+ "Ka 0.0041 0.1123 0.1224\n" +
+ "Kd 0.0776 0.2571 0.2041\n" +
+ "Ks 0.1467 0.1469 0.0965\n" +
+ "illum 2\n" +
+ "Ns 25.0000\n" +
+ "\n" +
+ "newmtl pinktan\n" +
+ "Ka 0.4408 0.2694 0.1592\n" +
+ "Kd 0.6857 0.2571 0.2163\n" +
+ "Ks 0.1467 0.1469 0.0965\n" +
+ "illum 2\n" +
+ "Ns 25.0000\n" +
+ "\n" +
+ "newmtl brnhair\n" +
+ "Ka 0.0612 0.0174 0.0066\n" +
+ "Kd 0.0898 0.0302 0.0110\n" +
+ "Ks 0.1306 0.0819 0.0352\n" +
+ "illum 2\n" +
+ "Ns 60.4700\n" +
+ "\n" +
+ "newmtl blondhair\n" +
+ "Ka 0.4449 0.2632 0.0509\n" +
+ "Kd 0.5714 0.3283 0.0443\n" +
+ "Ks 0.7755 0.4602 0.0918\n" +
+ "illum 2\n" +
+ "Ns 4.6500\n" +
+ "\n" +
+ "newmtl flblonde\n" +
+ "Ka 0.4449 0.2632 0.0509\n" +
+ "Kd 0.5714 0.3283 0.0443\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl yelloworng\n" +
+ "Ka 0.5837 0.1715 0.0000\n" +
+ "Kd 0.8857 0.2490 0.0000\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl bone\n" +
+ "Ka 0.3061 0.1654 0.0650\n" +
+ "Kd 0.9000 0.7626 0.4261\n" +
+ "Ks 0.8939 0.7609 0.5509\n" +
+ "illum 2\n" +
+ "Ns 200.0000\n" +
+ "\n" +
+ "newmtl teeth\n" +
+ "Ka 0.6408 0.5554 0.3845\n" +
+ "Kd 0.9837 0.7959 0.4694\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl brass\n" +
+ "Ka 0.2490 0.1102 0.0000\n" +
+ "Kd 0.4776 0.1959 0.0000\n" +
+ "Ks 0.5796 0.5796 0.5796\n" +
+ "illum 2\n" +
+ "Ns 134.8800\n" +
+ "\n" +
+ "newmtl dkred\n" +
+ "Ka 0.0939 0.0000 0.0000\n" +
+ "Kd 0.2286 0.0000 0.0000\n" +
+ "Ks 0.2490 0.0000 0.0000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl taupe\n" +
+ "Ka 0.1061 0.0709 0.0637\n" +
+ "Kd 0.2041 0.1227 0.1058\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 84.5000\n" +
+ "\n" +
+ "newmtl dkteal\n" +
+ "Ka 0.0000 0.0245 0.0163\n" +
+ "Kd 0.0000 0.0653 0.0449\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 55.0400\n" +
+ "\n" +
+ "newmtl dkdkgrey\n" +
+ "Ka 0.0000 0.0000 0.0000\n" +
+ "Kd 0.0122 0.0122 0.0122\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl dkblue\n" +
+ "Ka 0.0000 0.0029 0.0408\n" +
+ "Kd 0.0000 0.0041 0.0571\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl gold\n" +
+ "Ka 0.7224 0.1416 0.0000\n" +
+ "Kd 1.0000 0.4898 0.0000\n" +
+ "Ks 0.7184 0.3695 0.3695\n" +
+ "illum 2\n" +
+ "Ns 123.2600\n" +
+ "\n" +
+ "newmtl redbrick\n" +
+ "Ka 0.1102 0.0067 0.0067\n" +
+ "Kd 0.3306 0.0398 0.0081\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flmustard\n" +
+ "Ka 0.4245 0.2508 0.0000\n" +
+ "Kd 0.8898 0.3531 0.0073\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flpinegreen\n" +
+ "Ka 0.0367 0.0612 0.0204\n" +
+ "Kd 0.1061 0.2163 0.0857\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl fldkred\n" +
+ "Ka 0.0939 0.0000 0.0000\n" +
+ "Kd 0.2286 0.0082 0.0082\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl fldkgreen2\n" +
+ "Ka 0.0025 0.0122 0.0014\n" +
+ "Kd 0.0245 0.0694 0.0041\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flmintgreen\n" +
+ "Ka 0.0408 0.1429 0.0571\n" +
+ "Kd 0.1306 0.2898 0.1673\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl olivegreen\n" +
+ "Ka 0.0167 0.0245 0.0000\n" +
+ "Kd 0.0250 0.0367 0.0000\n" +
+ "Ks 0.2257 0.2776 0.1167\n" +
+ "illum 2\n" +
+ "Ns 97.6700\n" +
+ "\n" +
+ "newmtl skin\n" +
+ "Ka 0.2286 0.0187 0.0187\n" +
+ "Kd 0.1102 0.0328 0.0139\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 17.8300\n" +
+ "\n" +
+ "newmtl redbrown\n" +
+ "Ka 0.1469 0.0031 0.0000\n" +
+ "Kd 0.2816 0.0060 0.0000\n" +
+ "Ks 0.3714 0.3714 0.3714\n" +
+ "illum 2\n" +
+ "Ns 141.0900\n" +
+ "\n" +
+ "newmtl deepgreen\n" +
+ "Ka 0.0000 0.0050 0.0000\n" +
+ "Kd 0.0000 0.0204 0.0050\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 113.1800\n" +
+ "\n" +
+ "newmtl flltolivegreen\n" +
+ "Ka 0.0167 0.0245 0.0000\n" +
+ "Kd 0.0393 0.0531 0.0100\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl jetflame\n" +
+ "Ka 0.7714 0.0000 0.0000\n" +
+ "Kd 0.9510 0.4939 0.0980\n" +
+ "Ks 0.8531 0.5222 0.0000\n" +
+ "illum 2\n" +
+ "Ns 132.5600\n" +
+ "\n" +
+ "newmtl brownskn\n" +
+ "Ka 0.0122 0.0041 0.0000\n" +
+ "Kd 0.0204 0.0082 0.0000\n" +
+ "Ks 0.0735 0.0508 0.0321\n" +
+ "illum 2\n" +
+ "Ns 20.1600\n" +
+ "\n" +
+ "newmtl greenskn\n" +
+ "Ka 0.0816 0.0449 0.0000\n" +
+ "Kd 0.0000 0.0735 0.0000\n" +
+ "Ks 0.0490 0.1224 0.0898\n" +
+ "illum 3\n" +
+ "Ns 46.5100\n" +
+ "sharpness 146.5100\n" +
+ "\n" +
+ "newmtl ltgrey\n" +
+ "Ka 0.5000 0.5000 0.5000\n" +
+ "Kd 0.3837 0.3837 0.3837\n" +
+ "Ks 0.5000 0.5000 0.5000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl bronze\n" +
+ "Ka 0.0449 0.0204 0.0000\n" +
+ "Kd 0.0653 0.0367 0.0122\n" +
+ "Ks 0.0776 0.0408 0.0000\n" +
+ "illum 3\n" +
+ "Ns 137.2100\n" +
+ "sharpness 125.5800\n" +
+ "\n" +
+ "newmtl bone1\n" +
+ "Ka 0.6408 0.5554 0.3845\n" +
+ "Kd 0.9837 0.7959 0.4694\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flwhite1\n" +
+ "Ka 0.9306 0.9306 0.9306\n" +
+ "Kd 1.0000 1.0000 1.0000\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flwhite\n" +
+ "Ka 0.6449 0.6116 0.5447\n" +
+ "Kd 0.9837 0.9309 0.8392\n" +
+ "Ks 0.8082 0.7290 0.5708\n" +
+ "illum 2\n" +
+ "Ns 200.0000\n" +
+ "\n" +
+ "newmtl shadow\n" +
+ "Kd 0.0350 0.0248 0.0194\n" +
+ "illum 0\n" +
+ "d 0.7500\n" +
+ "\n" +
+ "newmtl fldkolivegreen\n" +
+ "Ka 0.0056 0.0082 0.0000\n" +
+ "Kd 0.0151 0.0204 0.0038\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl fldkdkgrey\n" +
+ "Ka 0.0000 0.0000 0.0000\n" +
+ "Kd 0.0122 0.0122 0.0122\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl lcdgreen\n" +
+ "Ka 0.4000 0.4000 0.4000\n" +
+ "Kd 0.5878 1.0000 0.5061\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl brownlips\n" +
+ "Ka 0.1143 0.0694 0.0245\n" +
+ "Kd 0.1429 0.0653 0.0408\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 25.0000\n" +
+ "\n" +
+ "newmtl muscle\n" +
+ "Ka 0.2122 0.0077 0.0154\n" +
+ "Kd 0.4204 0.0721 0.0856\n" +
+ "Ks 0.1184 0.1184 0.1184\n" +
+ "illum 2\n" +
+ "Ns 25.5800\n" +
+ "\n" +
+ "newmtl flltgrey\n" +
+ "Ka 0.5224 0.5224 0.5224\n" +
+ "Kd 0.8245 0.8245 0.8245\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl offwhite.warm\n" +
+ "Ka 0.5184 0.4501 0.3703\n" +
+ "Kd 0.8367 0.6898 0.4490\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl offwhite.cool\n" +
+ "Ka 0.5184 0.4501 0.3703\n" +
+ "Kd 0.8367 0.6812 0.5703\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl yellowbrt\n" +
+ "Ka 0.4000 0.4000 0.4000\n" +
+ "Kd 1.0000 0.7837 0.0000\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl chappie\n" +
+ "Ka 0.4000 0.4000 0.4000\n" +
+ "Kd 0.5837 0.1796 0.0367\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl archwhite\n" +
+ "Ka 0.2816 0.2816 0.2816\n" +
+ "Kd 0.9959 0.9959 0.9959\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl archwhite2\n" +
+ "Ka 0.2816 0.2816 0.2816\n" +
+ "Kd 0.8408 0.8408 0.8408\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl lighttan\n" +
+ "Ka 0.0980 0.0536 0.0220\n" +
+ "Kd 0.7020 0.4210 0.2206\n" +
+ "Ks 0.8286 0.8057 0.5851\n" +
+ "illum 2\n" +
+ "Ns 177.5200\n" +
+ "\n" +
+ "newmtl lighttan2\n" +
+ "Ka 0.0980 0.0492 0.0144\n" +
+ "Kd 0.3143 0.1870 0.0962\n" +
+ "Ks 0.8286 0.8057 0.5851\n" +
+ "illum 2\n" +
+ "Ns 177.5200\n" +
+ "\n" +
+ "newmtl lighttan3\n" +
+ "Ka 0.0980 0.0492 0.0144\n" +
+ "Kd 0.1796 0.0829 0.0139\n" +
+ "Ks 0.8286 0.8057 0.5851\n" +
+ "illum 2\n" +
+ "Ns 177.5200\n" +
+ "\n" +
+ "newmtl lightyellow\n" +
+ "Ka 0.5061 0.1983 0.0000\n" +
+ "Kd 1.0000 0.9542 0.3388\n" +
+ "Ks 1.0000 0.9060 0.0000\n" +
+ "illum 2\n" +
+ "Ns 177.5200\n" +
+ "\n" +
+ "newmtl lighttannew\n" +
+ "Ka 0.0980 0.0492 0.0144\n" +
+ "Kd 0.7878 0.6070 0.3216\n" +
+ "Ks 0.8286 0.8057 0.5851\n" +
+ "illum 2\n" +
+ "Ns 177.5200\n" +
+ "\n" +
+ "newmtl default\n" +
+ "Ka 0.4000 0.4000 0.4000\n" +
+ "Kd 0.7102 0.7020 0.6531\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 128.0000\n" +
+ "\n" +
+ "newmtl ship2\n" +
+ "Ka 0.0000 0.0000 0.0000\n" +
+ "Kd 1.0000 1.0000 1.0000\n" +
+ "Ks 0.1143 0.1143 0.1143\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl dkpurple\n" +
+ "Ka 0.0082 0.0000 0.0163\n" +
+ "Kd 0.0245 0.0000 0.0490\n" +
+ "Ks 0.1266 0.0000 0.2531\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl dkorange\n" +
+ "Ka 0.4041 0.0123 0.0000\n" +
+ "Kd 0.7143 0.0350 0.0000\n" +
+ "Ks 0.7102 0.0870 0.0000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl mintgrn\n" +
+ "Ka 0.0101 0.1959 0.0335\n" +
+ "Kd 0.0245 0.4776 0.0816\n" +
+ "Ks 0.0245 0.4776 0.0816\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl fgreen\n" +
+ "Ka 0.0000 0.0449 0.0000\n" +
+ "Kd 0.0000 0.0449 0.0004\n" +
+ "Ks 0.0062 0.0694 0.0000\n" +
+ "illum 2\n" +
+ "Ns 106.2000\n" +
+ "\n" +
+ "newmtl glassblutint\n" +
+ "Ka 0.4000 0.4000 0.4000\n" +
+ "Kd 0.5551 0.8000 0.7730\n" +
+ "Ks 0.7969 0.9714 0.9223\n" +
+ "illum 4\n" +
+ "d 0.3300\n" +
+ "Ns 60.0000\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl bflesh\n" +
+ "Ka 0.0122 0.0122 0.0122\n" +
+ "Kd 0.0245 0.0081 0.0021\n" +
+ "Ks 0.0531 0.0460 0.0153\n" +
+ "illum 2\n" +
+ "Ns 20.1600\n" +
+ "\n" +
+ "newmtl meh\n" +
+ "Ka 0.4000 0.4000 0.4000\n" +
+ "Kd 0.5551 0.8000 0.7730\n" +
+ "Ks 0.7969 0.9714 0.9223\n" +
+ "illum 4\n" +
+ "d 0.7500\n" +
+ "Ns 183.7200\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl violet\n" +
+ "Ka 0.0083 0.0000 0.1265\n" +
+ "Kd 0.0287 0.0269 0.1347\n" +
+ "Ks 0.2267 0.4537 0.6612\n" +
+ "illum 2\n" +
+ "Ns 96.9000\n" +
+ "\n" +
+ "newmtl iris\n" +
+ "Ka 0.3061 0.0556 0.0037\n" +
+ "Kd 0.0000 0.0572 0.3184\n" +
+ "Ks 0.8041 0.6782 0.1477\n" +
+ "illum 2\n" +
+ "Ns 188.3700\n" +
+ "\n" +
+ "newmtl blugrn\n" +
+ "Ka 0.4408 0.4144 0.1592\n" +
+ "Kd 0.0811 0.6408 0.2775\n" +
+ "Ks 0.1467 0.1469 0.0965\n" +
+ "illum 2\n" +
+ "Ns 25.0000\n" +
+ "\n" +
+ "newmtl glasstransparent\n" +
+ "Ka 0.2163 0.2163 0.2163\n" +
+ "Kd 0.4694 0.4694 0.4694\n" +
+ "Ks 0.6082 0.6082 0.6082\n" +
+ "illum 4\n" +
+ "d 0.7500\n" +
+ "Ns 200.0000\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl fleshtransparent\n" +
+ "Ka 0.4000 0.2253 0.2253\n" +
+ "Kd 0.6898 0.2942 0.1295\n" +
+ "Ks 0.7388 0.4614 0.4614\n" +
+ "illum 4\n" +
+ "d 0.7500\n" +
+ "Ns 6.2000\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl fldkgrey\n" +
+ "Ka 0.0449 0.0449 0.0449\n" +
+ "Kd 0.0939 0.0939 0.0939\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl sky_blue\n" +
+ "Ka 0.1363 0.2264 0.4122\n" +
+ "Kd 0.1241 0.5931 0.8000\n" +
+ "Ks 0.0490 0.0490 0.0490\n" +
+ "illum 2\n" +
+ "Ns 13.9500\n" +
+ "\n" +
+ "newmtl fldkpurple\n" +
+ "Ka 0.0443 0.0257 0.0776\n" +
+ "Kd 0.1612 0.0000 0.3347\n" +
+ "Ks 0.0000 0.0000 0.0000\n" +
+ "illum 2\n" +
+ "Ns 13.9500\n" +
+ "\n" +
+ "newmtl dkbrown\n" +
+ "Ka 0.0143 0.0062 0.0027\n" +
+ "Kd 0.0087 0.0038 0.0016\n" +
+ "Ks 0.2370 0.2147 0.1821\n" +
+ "illum 3\n" +
+ "Ns 60.0000\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl bone2\n" +
+ "Ka 0.6408 0.5388 0.3348\n" +
+ "Kd 0.9837 0.8620 0.6504\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl bluegrey\n" +
+ "Ka 0.4000 0.4000 0.4000\n" +
+ "Kd 0.1881 0.2786 0.2898\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 14.7300\n" +
+ "\n" +
+ "newmtl metal\n" +
+ "Ka 0.9102 0.8956 0.1932\n" +
+ "Kd 0.9000 0.7626 0.4261\n" +
+ "Ks 0.8939 0.8840 0.8683\n" +
+ "illum 2\n" +
+ "Ns 200.0000\n" +
+ "\n" +
+ "newmtl sand_stone\n" +
+ "Ka 0.1299 0.1177 0.0998\n" +
+ "Kd 0.1256 0.1138 0.0965\n" +
+ "Ks 0.2370 0.2147 0.1821\n" +
+ "illum 3\n" +
+ "Ns 60.0000\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl hair\n" +
+ "Ka 0.0013 0.0012 0.0010\n" +
+ "Kd 0.0008 0.0007 0.0006\n" +
+ "Ks 0.0000 0.0000 0.0000\n" +
+ "illum 3\n" +
+ "Ns 60.0000\n" +
+ "sharpness 60.0000\n";
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFile.java b/src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFile.java
new file mode 100644
index 0000000..d3fcd67
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFile.java
@@ -0,0 +1,1334 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.objectfile;
+
+import com.sun.j3d.loaders.Scene;
+import com.sun.j3d.loaders.SceneBase;
+import com.sun.j3d.loaders.Loader;
+import com.sun.j3d.loaders.IncorrectFormatException;
+import com.sun.j3d.loaders.ParsingErrorException;
+import com.sun.j3d.loaders.objectfile.ObjectFileParser;
+import com.sun.j3d.loaders.objectfile.ObjectFileMaterials;
+import com.sun.j3d.utils.geometry.GeometryInfo;
+import com.sun.j3d.utils.geometry.NormalGenerator;
+import com.sun.j3d.utils.geometry.Stripifier;
+import java.io.FileNotFoundException;
+import java.io.StreamTokenizer;
+import java.io.Reader;
+import java.io.BufferedReader;
+import java.io.BufferedInputStream;
+import java.io.FileReader;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+import javax.media.j3d.*;
+import javax.vecmath.Color3f;
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3f;
+import javax.vecmath.TexCoord2f;
+import java.net.MalformedURLException;
+
+
+
+/**
+ * The ObjectFile class implements the Loader interface for the Wavefront
+ * .obj file format, a standard 3D object file format created for use with
+ * Wavefront's Advanced Visualizer (tm) and available for purchase from
+ * Viewpoint DataLabs, as well as other 3D model companies. Object Files
+ * are text based
+ * files supporting both polygonal and free-form geometry (curves
+ * and surfaces). The Java 3D .obj file loader supports a subset of the
+ * file format, but it is enough to load almost all commonly available
+ * Object Files. Free-form geometry is not supported.</p>
+ *
+ * The Object File tokens currently supported by this loader are:</p>
+ * <code>v <i>float</i> <i>float</i> <i>float</i></code></p>
+ * <dl><dd>A single vertex's geometric position in space. The first vertex
+ * listed in the file has index 1,
+ * and subsequent vertices are numbered sequentially.</dl></p>
+ * <code>vn <i>float</i> <i>float</i> <i>float</i></code></p>
+ * <dl><dd>A normal. The first normal in the file is index 1, and
+ * subsequent normals are numbered sequentially.</dl></p>
+ * <code>vt <i>float</i> <i>float</i></code></p>
+ * <dl><dd>A texture coordinate. The first texture coordinate in the file is
+ * index 1, and subsequent normals are numbered sequentially.</dl></p>
+ * <code>f <i>int</i> <i>int</i> <i>int</i> . . .</code></p>
+ * <dl><dd><i><b>or</b></i></dl></p>
+ * <code>f <i>int</i>/<i>int</i> <i>int</i>/<i>int</i> <i>int</i>/<i>int</i> . . .</code></p>
+ * <dl><dd><i><b>or</i></b></dl></p>
+ * <code>f <i>int</i>/<i>int</i>/<i>int</i> <i>int</i>/<i>int</i>/<i>int</i> <i>int</i>/<i>int</i>/<i>int</i> . . .</code></p>
+ * <dl><dd>A polygonal face. The numbers are indexes into the arrays of
+ * vertex positions, texture coordinates, and normals respectively.
+ * There is no maximum number of vertices that a single polygon may
+ * contain. The .obj file specification says that each face must
+ * be flat and convex, but if the TRIANGULATE flag is sent to the
+ * ObjectFile constructor, each face will be triangulated by the
+ * Java 3D Triangulator, and therefore may be concave.
+ * A number may be omitted if, for example, texture coordinates are
+ * not being defined in the model. Numbers are normally positive
+ * indexes, but may also be negative. An index of -1 means the last
+ * member added to the respective array, -2 is the one before that,
+ * and so on.</dl></p>
+ * <code>g <i>name</i></code></p>
+ * <dl><dd>Faces defined after this token will be added to the named group.
+ * These geometry groups are returned as separated Shape3D objects
+ * attached to the parent SceneGroup. Each named Shape3D will also
+ * be in the Hashtable returned by Scene.getNamedObjects(). It is
+ * legal to add faces to a group, switch to another group, and then
+ * add more faces to the original group by reissuing the same name
+ * with the g token. If faces are added to the model before the g
+ * token is seen, the faces are put into the default group called
+ * "default."</dl></p>
+ * <code>s <i>int</i></code></p>
+ * <dl><dd><i><b>or</i></b></dl></p>
+ * <code>s off</code></p>
+ * <dl><dd>If the vn token is not used in the file to specify vertex normals
+ * for the model, this token may be used to put faces into groups
+ * for normal calculation ("smoothing groups") in the same manner as
+ * the 'g' token
+ * is used to group faces geometrically. Faces in the same smoothing
+ * group will have their normals calculated as if they are part of
+ * the same smooth surface. To do this, we use the Java 3D NormalGenerator
+ * utility with the creaseAngle parameter set to PI (180 degrees -
+ * smooth shading, no creases) or to whatever the user has set the
+ * creaseAngle. Faces in group 0 or 'off' use a
+ * creaseAngle of zero, meaning there is no smoothing (the normal
+ * of the face is used at all vertices giving the surface a faceted
+ * look; there will be a
+ * crease, or "Hard Edge," between each face in group zero). There is
+ * also an implied hard edge <i>between</i> each smoothing group, where they
+ * meet each other.</p>
+ * </p>
+ * If neither the vn nor the s token is used in the file, then normals
+ * are calculated using the creaseAngle set in the contructor.
+ * Normals are calculated on each geometry
+ * group separately, meaning there will be a hard edge between each
+ * geometry group.</dl></p>
+ * </p>
+ * <code>usemtl <i>name</i></code></p>
+ * <dl><dd>The current (and subsequent) geometry groups (specified with
+ * the 'g' token) have applied
+ * to them the named material property. The following set of material
+ * properties are available by default:</dl></p>
+ * <pre>
+ * amber amber_trans aqua aqua_filter
+ * archwhite archwhite2 bflesh black
+ * blondhair blue_pure bluegrey bluetint
+ * blugrn blutan bluteal bone
+ * bone1 bone2 brass brnhair
+ * bronze brown brownlips brownskn
+ * brzskin chappie charcoal deepgreen
+ * default dkblue dkblue_pure dkbrown
+ * dkdkgrey dkgreen dkgrey dkorange
+ * dkpurple dkred dkteal emerald
+ * fgreen flaqua flblack flblonde
+ * flblue_pure flbrown fldkblue_pure fldkdkgrey
+ * fldkgreen fldkgreen2 fldkgrey fldkolivegreen
+ * fldkpurple fldkred flesh fleshtransparent
+ * flgrey fllime flltbrown flltgrey
+ * flltolivegreen flmintgreen flmustard florange
+ * flpinegreen flpurple flred fltan
+ * flwhite flwhite1 flyellow glass
+ * glassblutint glasstransparent gold green
+ * greenskn grey hair iris
+ * jetflame lavendar lcdgreen lighttan
+ * lighttan2 lighttan3 lighttannew lightyellow
+ * lime lips ltbrown ltgrey
+ * meh metal mintgrn muscle
+ * navy_blue offwhite.cool offwhite.warm olivegreen
+ * orange pale_green pale_pink pale_yellow
+ * peach periwinkle pink pinktan
+ * plasma purple red redbrick
+ * redbrown redorange redwood rubber
+ * ruby sand_stone sapphire shadow
+ * ship2 silver skin sky_blue
+ * smoked_glass tan taupe teeth
+ * violet white yellow yellow_green
+ * yellowbrt yelloworng
+ * </pre>
+ * <code>mtllib <i>filename</i></code></p>
+ * <dl><dd>Load material properties from the named file. Materials
+ * with the same name as the predefined materials above will override
+ * the default value. Any directory path information in (filename)
+ * is ignored. The .mtl files are assumed to be in the same directory
+ * as the .obj file. If they are in a different directory, use
+ * Loader.setBasePath() (or Loader.setBaseUrl() ). The format of the
+ * material properties files
+ * are as follows:</p>
+ * <code>newmtl <i>name</i></code></p>
+ * <dl><dd>Start the definition of a new named material property.</dl></p>
+ * <code>Ka <i>float</i> <i>float</i> <i>float</i></code></p>
+ * <dl><dd>Ambient color.</dl></p>
+ * <code>Kd <i>float</i> <i>float</i> <i>float</i></code></p>
+ * <dl><dd>Diffuse color.</dl></p>
+ * <code>Ks <i>float</i> <i>float</i> <i>float</i></code></p>
+ * <dl><dd>Specular color.</dl></p>
+ * <code>illum <i>(0, 1, or 2)</i></code></p>
+ * <dl><dd>0 to disable lighting, 1 for ambient & diffuse only (specular
+ * color set to black), 2 for full lighting.</dl></p>
+ * <code>Ns <i>float</i></code></p>
+ * <dl><dd>Shininess (clamped to 1.0 - 128.0).</dl></p>
+ * <code>map_Kd <i>filename</i></code></p>
+ * <dl><dd>Texture map. Supports .rgb, .rgba, .int, .inta, .sgi, and
+ * .bw files in addition to those supported by
+ * <a href="../../utils/image/TextureLoader.html">TextureLoader</a>.
+ * </dl></dl></p>
+ */
+
+public class ObjectFile implements Loader {
+ // 0=Input file assumed good
+ // 1=Input file checked for inconsistencies
+ // 2=path names
+ // 4=flags
+ // 8=Timing Info
+ // 16=Tokens
+ // 32=Token details (use with 16)
+ // 64=limits of model coordinates
+ private static final int DEBUG = 1;
+
+ /**
+ * Flag sent to constructor. The object's vertices will be changed
+ * so that the object is centered at (0,0,0) and the coordinate
+ * positions are all in the range of (-1,-1,-1) to (1,1,1).
+ */
+ public static final int RESIZE = LOAD_SOUND_NODES << 1;
+
+ /**
+ * Flag sent to constructor. The Shape3D object will be created
+ * by using the GeometryInfo POLYGON_ARRAY primitive, causing
+ * them to be Triangulated by GeometryInfo. Use
+ * this if you suspect concave or other non-behaving polygons
+ * in your model.
+ */
+ public static final int TRIANGULATE = RESIZE << 1;
+
+ /**
+ * Flag sent to constructor. Use if the vertices in your .obj
+ * file were specified with clockwise winding (Java 3D wants
+ * counter-clockwise) so you see the back of the polygons and
+ * not the front. Calls GeometryInfo.reverse().
+ */
+ public static final int REVERSE = TRIANGULATE << 1;
+
+ /**
+ * Flag sent to contructor. After normals are generated the data
+ * will be analyzed to find triangle strips. Use this if your
+ * hardware supports accelerated rendering of strips.
+ */
+ public static final int STRIPIFY = REVERSE << 1;
+
+ private static final char BACKSLASH = '\\';
+
+ private int flags;
+ private String basePath = null;
+ private URL baseUrl = null;
+ private boolean fromUrl = false;
+ private float radians;
+
+ // First, lists of points are read from the .obj file into these arrays. . .
+ private ArrayList coordList; // Holds Point3f
+ private ArrayList texList; // Holds TexCoord2f
+ private ArrayList normList; // Holds Vector3f
+
+ // . . . and index lists are read into these arrays.
+ private ArrayList coordIdxList; // Holds Integer index into coordList
+ private ArrayList texIdxList; // Holds Integer index into texList
+ private ArrayList normIdxList; // Holds Integer index into normList
+
+ // The length of each face is stored in this array.
+ private ArrayList stripCounts; // Holds Integer
+
+ // Each face's Geometry Group membership is kept here. . .
+ private HashMap groups; // key=Integer index into stripCounts
+ // value=String name of group
+ private String curGroup;
+
+ // . . . and Smoothing Group membership is kept here
+ private HashMap sGroups; // key=Integer index into stripCounts
+ // value=String name of group
+ private String curSgroup;
+
+ // The name of each group's "usemtl" material property is kept here
+ private HashMap groupMaterials; // key=String name of Group
+ // value=String name of material
+
+
+ // After reading the entire file, the faces are converted into triangles.
+ // The Geometry Group information is converted into these structures. . .
+ private HashMap triGroups; // key=String name of group
+ // value=ArrayList of Integer
+ // indices into coordIdxList
+ private ArrayList curTriGroup;
+
+ // . . . and Smoothing Group info is converted into these.
+ private HashMap triSgroups; // key=String name of group
+ // value=ArrayList of Integer
+ // indices into coordIdxList
+ private ArrayList curTriSgroup;
+
+
+ // Finally, coordList, texList, and normList are converted to arrays for
+ // use with GeometryInfo
+ private Point3f coordArray[] = null;
+ private Vector3f normArray[] = null;
+ private TexCoord2f texArray[] = null;
+
+ // Used for debugging
+ private long time;
+
+ private ObjectFileMaterials materials = null;
+
+
+ void readVertex(ObjectFileParser st) throws ParsingErrorException {
+ Point3f p = new Point3f();
+
+ st.getNumber();
+ p.x = (float)st.nval;
+ st.getNumber();
+ p.y = (float)st.nval;
+ st.getNumber();
+ p.z = (float)st.nval;
+
+ if ((DEBUG & 32) != 0)
+ System.out.println(" (" + p.x + "," + p.y + "," + p.z + ")");
+
+ st.skipToNextLine();
+
+ // Add this vertex to the array
+ coordList.add(p);
+ } // End of readVertex
+
+
+ /**
+ * readNormal
+ */
+ void readNormal(ObjectFileParser st) throws ParsingErrorException {
+ Vector3f p = new Vector3f();
+
+ st.getNumber();
+ p.x = (float)st.nval;
+ st.getNumber();
+ p.y = (float)st.nval;
+ st.getNumber();
+ p.z = (float)st.nval;
+
+ if ((DEBUG & 32) != 0)
+ System.out.println(" (" + p.x + "," + p.y + "," + p.z + ")");
+
+ st.skipToNextLine();
+
+ // Add this vertex to the array
+ normList.add(p);
+ } // End of readNormal
+
+
+ /**
+ * readTexture
+ */
+ void readTexture(ObjectFileParser st) throws ParsingErrorException {
+ TexCoord2f p = new TexCoord2f();
+
+ st.getNumber();
+ p.x = (float)st.nval;
+ st.getNumber();
+ p.y = (float)st.nval;
+
+ if ((DEBUG & 32) != 0)
+ System.out.println(" (" + p.x + "," + p.y + ")");
+
+ st.skipToNextLine();
+
+ // Add this vertex to the array
+ texList.add(p);
+ } // End of readTexture
+
+
+ /**
+ * readFace
+ *
+ * Adds the indices of the current face to the arrays.
+ *
+ * ViewPoint files can have up to three arrays: Vertex Positions,
+ * Texture Coordinates, and Vertex Normals. Each vertex can
+ * contain indices into all three arrays.
+ */
+ void readFace(ObjectFileParser st) throws ParsingErrorException {
+ int vertIndex, texIndex = 0, normIndex = 0;
+ int count = 0;
+
+ // There are n vertices on each line. Each vertex is comprised
+ // of 1-3 numbers separated by slashes ('/'). The slashes may
+ // be omitted if there's only one number.
+
+ st.getToken();
+
+ while (st.ttype != st.TT_EOL) {
+ // First token is always a number (or EOL)
+ st.pushBack();
+ st.getNumber();
+ vertIndex = (int)st.nval - 1;
+ if (vertIndex < 0) vertIndex += coordList.size() + 1;
+ coordIdxList.add(new Integer(vertIndex));
+
+ // Next token is a slash, a number, or EOL. Continue on slash
+ st.getToken();
+ if (st.ttype == '/') {
+
+ // If there's a number after the first slash, read it
+ st.getToken();
+ if (st.ttype == st.TT_WORD) {
+ // It's a number
+ st.pushBack();
+ st.getNumber();
+ texIndex = (int)st.nval - 1;
+ if (texIndex < 0) texIndex += texList.size() + 1;
+ texIdxList.add(new Integer(texIndex));
+ st.getToken();
+ }
+
+ // Next token is a slash, a number, or EOL. Continue on slash
+ if (st.ttype == '/') {
+
+ // There has to be a number after the 2nd slash
+ st.getNumber();
+ normIndex = (int)st.nval - 1;
+ if (normIndex < 0) normIndex += normList.size() + 1;
+ normIdxList.add(new Integer(normIndex));
+ st.getToken();
+ }
+ }
+ if ((DEBUG & 32) != 0) {
+ System.out.println(" " + vertIndex + '/' + texIndex +
+ '/' + normIndex);
+ }
+ count++;
+ }
+
+ Integer faceNum = new Integer(stripCounts.size());
+ stripCounts.add(new Integer(count));
+
+ // Add face to current groups
+ groups.put(faceNum, curGroup);
+ if (curSgroup != null) sGroups.put(faceNum, curSgroup);
+
+ // In case we exited early
+ st.skipToNextLine();
+ } // End of readFace
+
+
+ /**
+ * readPartName
+ */
+ void readPartName(ObjectFileParser st) {
+ st.getToken();
+
+ // Find the Material Property of the current group
+ String curMat = (String)groupMaterials.get(curGroup);
+
+ // New faces will be added to the curGroup
+ if (st.ttype != ObjectFileParser.TT_WORD) curGroup = "default";
+ else curGroup = st.sval;
+ if ((DEBUG & 32) != 0) System.out.println(" Changed to group " + curGroup);
+
+ // See if this group has Material Properties yet
+ if (groupMaterials.get(curGroup) == null) {
+ // It doesn't - carry over from last group
+ groupMaterials.put(curGroup, curMat);
+ }
+
+ st.skipToNextLine();
+ } // End of readPartName
+
+
+ /**
+ * readMaterialName
+ */
+ void readMaterialName(ObjectFileParser st) throws ParsingErrorException {
+ st.getToken();
+ if (st.ttype == ObjectFileParser.TT_WORD) {
+ groupMaterials.put(curGroup, new String(st.sval));
+ if ((DEBUG & 32) != 0) {
+ System.out.println(" Material Property " + st.sval +
+ " assigned to group " + curGroup);
+ }
+ }
+ st.skipToNextLine();
+ } // End of readMaterialName
+
+
+ /**
+ * loadMaterialFile
+ *
+ * Both types of slashes are returned as tokens from our parser,
+ * so we go through the line token by token and keep just the
+ * last token on the line. This should be the filename without
+ * any directory info.
+ */
+ void loadMaterialFile(ObjectFileParser st) throws ParsingErrorException {
+ String s = null;
+
+ // Filenames are case sensitive
+ st.lowerCaseMode(false);
+
+ // Get name of material file (skip path)
+ do {
+ st.getToken();
+ if (st.ttype == ObjectFileParser.TT_WORD) s = st.sval;
+ } while (st.ttype != ObjectFileParser.TT_EOL);
+
+ materials.readMaterialFile(fromUrl,
+ fromUrl ? baseUrl.toString() : basePath, s);
+
+ st.lowerCaseMode(true);
+ st.skipToNextLine();
+ } // End of loadMaterialFile
+
+
+ /**
+ * readSmoothingGroup
+ */
+ void readSmoothingGroup(ObjectFileParser st) throws ParsingErrorException {
+ st.getToken();
+ if (st.ttype != ObjectFileParser.TT_WORD) {
+ st.skipToNextLine();
+ return;
+ }
+ if (st.sval.equals("off")) curSgroup = "0";
+ else curSgroup = st.sval;
+ if ((DEBUG & 32) != 0) System.out.println(" Smoothing group " + curSgroup);
+ st.skipToNextLine();
+ } // End of readSmoothingGroup
+
+
+ /**
+ * readFile
+ *
+ * Read the model data from the file.
+ */
+ void readFile(ObjectFileParser st) throws ParsingErrorException {
+ int t;
+
+ st.getToken();
+ while (st.ttype != ObjectFileParser.TT_EOF) {
+
+ // Print out one token for each line
+ if ((DEBUG & 16) != 0) {
+ System.out.print("Token ");
+ if (st.ttype == ObjectFileParser.TT_EOL) System.out.println("EOL");
+ else if (st.ttype == ObjectFileParser.TT_WORD)
+ System.out.println(st.sval);
+ else System.out.println((char)st.ttype);
+ }
+
+ if (st.ttype == ObjectFileParser.TT_WORD) {
+ if (st.sval.equals("v")) {
+ readVertex(st);
+ } else if (st.sval.equals("vn")) {
+ readNormal(st);
+ } else if (st.sval.equals("vt")) {
+ readTexture(st);
+ } else if (st.sval.equals("f")) {
+ readFace(st);
+ } else if (st.sval.equals("fo")) { // Not sure what the dif is
+ readFace(st);
+ } else if (st.sval.equals("g")) {
+ readPartName(st);
+ } else if (st.sval.equals("s")) {
+ readSmoothingGroup(st);
+ } else if (st.sval.equals("p")) {
+ st.skipToNextLine();
+ } else if (st.sval.equals("l")) {
+ st.skipToNextLine();
+ } else if (st.sval.equals("mtllib")) {
+ loadMaterialFile(st);
+ } else if (st.sval.equals("usemtl")) {
+ readMaterialName(st);
+ } else if (st.sval.equals("maplib")) {
+ st.skipToNextLine();
+ } else if (st.sval.equals("usemap")) {
+ st.skipToNextLine();
+ } else {
+ throw new ParsingErrorException(
+ "Unrecognized token, line " + st.lineno());
+ }
+ }
+
+ st.skipToNextLine();
+
+ // Get next token
+ st.getToken();
+ }
+ } // End of readFile
+
+
+ /**
+ * Constructor.
+ *
+ * @param flags The constants from above or from
+ * com.sun.j3d.loaders.Loader, possibly "or'ed" (|) together.
+ * @param radians Ignored if the vn token is present in the model (user
+ * normals supplied). Otherwise, crease angle to use within smoothing
+ * groups, or within geometry groups if the s token isn't present either.
+ */
+ public ObjectFile(int flags, float radians) {
+ setFlags(flags);
+ this.radians = radians;
+ } // End of ObjectFile(int, float)
+
+
+ /**
+ * Constructor. Crease Angle set to default of
+ * 44 degrees (see NormalGenerator utility for details).
+ * @param flags The constants from above or from
+ * com.sun.j3d.loaders.Loader, possibly "or'ed" (|) together.
+ */
+ public ObjectFile(int flags) {
+ this(flags, -1.0f);
+ } // End of ObjectFile(int)
+
+
+ /**
+ * Default constructor. Crease Angle set to default of
+ * 44 degrees (see NormalGenerator utility for details). Flags
+ * set to zero (0).
+ */
+ public ObjectFile() {
+ this(0, -1.0f);
+ } // End of ObjectFile()
+
+
+ /**
+ * Takes a file name and sets the base path to the directory
+ * containing that file.
+ */
+ private void setBasePathFromFilename(String fileName) {
+ if (fileName.lastIndexOf(java.io.File.separator) == -1) {
+ // No path given - current directory
+ setBasePath("." + java.io.File.separator);
+ } else {
+ setBasePath(
+ fileName.substring(0, fileName.lastIndexOf(java.io.File.separator)));
+ }
+ } // End of setBasePathFromFilename
+
+
+ /**
+ * The Object File is loaded from the .obj file specified by
+ * the filename.
+ * To attach the model to your scene, call getSceneGroup() on
+ * the Scene object passed back, and attach the returned
+ * BranchGroup to your scene graph. For an example, see
+ * j3d-examples/ObjLoad/ObjLoad.java.
+ */
+ public Scene load(String filename) throws FileNotFoundException,
+ IncorrectFormatException,
+ ParsingErrorException {
+
+ setBasePathFromFilename(filename);
+
+ Reader reader = new BufferedReader(new FileReader(filename));
+ return load(reader);
+ } // End of load(String)
+
+
+ private void setBaseUrlFromUrl(URL url) throws FileNotFoundException {
+ String u = url.toString();
+ String s;
+ if (u.lastIndexOf('/') == -1) {
+ s = url.getProtocol() + ":";
+ } else {
+ s = u.substring(0, u.lastIndexOf('/') + 1);
+ }
+ try {
+ baseUrl = new URL(s);
+ }
+ catch (MalformedURLException e) {
+ throw new FileNotFoundException(e.getMessage());
+ }
+ } // End of setBaseUrlFromUrl
+
+
+ /**
+ * The object file is loaded off of the web.
+ * To attach the model to your scene, call getSceneGroup() on
+ * the Scene object passed back, and attach the returned
+ * BranchGroup to your scene graph. For an example, see
+ * j3d-examples/ObjLoad/ObjLoad.java.
+ */
+ public Scene load(URL url) throws FileNotFoundException,
+ IncorrectFormatException,
+ ParsingErrorException {
+ BufferedReader reader;
+
+ if (baseUrl == null) setBaseUrlFromUrl(url);
+
+ try {
+ reader = new BufferedReader(new InputStreamReader(url.openStream()));
+ }
+ catch (IOException e) {
+ throw new FileNotFoundException(e.getMessage());
+ }
+ fromUrl = true;
+ return load(reader);
+ } // End of load(URL)
+
+
+ /**
+ * getLimits
+ *
+ * Returns an array of Point3f which form a bounding box around the
+ * object. Element 0 is the low value, element 1 is the high value.
+ * See normalize() below for an example of how to use this method.
+ */
+ private Point3f[] getLimits() {
+ Point3f cur_vtx = new Point3f();
+
+ // Find the limits of the model
+ Point3f[] limit = new Point3f[2];
+ limit[0] = new Point3f(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE);
+ limit[1] = new Point3f(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE);
+ for (int i = 0 ; i < coordList.size() ; i++) {
+
+ cur_vtx = (Point3f)coordList.get(i);
+
+ // Keep track of limits for normalization
+ if (cur_vtx.x < limit[0].x) limit[0].x = cur_vtx.x;
+ if (cur_vtx.x > limit[1].x) limit[1].x = cur_vtx.x;
+ if (cur_vtx.y < limit[0].y) limit[0].y = cur_vtx.y;
+ if (cur_vtx.y > limit[1].y) limit[1].y = cur_vtx.y;
+ if (cur_vtx.z < limit[0].z) limit[0].z = cur_vtx.z;
+ if (cur_vtx.z > limit[1].z) limit[1].z = cur_vtx.z;
+ }
+
+ if ((DEBUG & 64) != 0) {
+ System.out.println("Model range: (" +
+ limit[0].x + "," + limit[0].y + "," + limit[0].z + ") to (" +
+ limit[1].x + "," + limit[1].y + "," + limit[1].z + ")");
+ }
+
+ return limit;
+ } // End of getLimits
+
+
+
+ /**
+ * Center the object and make it (-1,-1,-1) to (1,1,1).
+ */
+ private void resize() {
+ int i, j;
+ Point3f cur_vtx = new Point3f();
+ float biggest_dif;
+
+ Point3f[] limit = getLimits();
+
+ // Move object so it's centered on (0,0,0)
+ Vector3f offset = new Vector3f(-0.5f * (limit[0].x + limit[1].x),
+ -0.5f * (limit[0].y + limit[1].y),
+ -0.5f * (limit[0].z + limit[1].z));
+
+ if ((DEBUG & 64) != 0) {
+ System.out.println("Offset amount: (" +
+ offset.x + "," + offset.y + "," + offset.z + ")");
+ }
+
+ // Find the divide-by value for the normalization
+ biggest_dif = limit[1].x - limit[0].x;
+ if (biggest_dif < limit[1].y - limit[0].y)
+ biggest_dif = limit[1].y - limit[0].y;
+ if (biggest_dif < limit[1].z - limit[0].z)
+ biggest_dif = limit[1].z - limit[0].z;
+ biggest_dif /= 2.0f;
+
+ for (i = 0 ; i < coordList.size() ; i++) {
+
+ cur_vtx = (Point3f)coordList.get(i);
+
+ cur_vtx.add(cur_vtx, offset);
+
+ cur_vtx.x /= biggest_dif;
+ cur_vtx.y /= biggest_dif;
+ cur_vtx.z /= biggest_dif;
+
+ // coordList.setElementAt(cur_vtx, i);
+ }
+ } // End of resize
+
+
+ private int[] objectToIntArray(ArrayList inList) {
+ int outList[] = new int[inList.size()];
+ for (int i = 0 ; i < inList.size() ; i++) {
+ outList[i] = ((Integer)inList.get(i)).intValue();
+ }
+ return outList;
+ } // End of objectToIntArray
+
+
+ private Point3f[] objectToPoint3Array(ArrayList inList) {
+ Point3f outList[] = new Point3f[inList.size()];
+ for (int i = 0 ; i < inList.size() ; i++) {
+ outList[i] = (Point3f)inList.get(i);
+ }
+ return outList;
+ } // End of objectToPoint3Array
+
+
+
+ private TexCoord2f[] objectToTexCoord2Array(ArrayList inList) {
+ TexCoord2f outList[] = new TexCoord2f[inList.size()];
+ for (int i = 0 ; i < inList.size() ; i++) {
+ outList[i] = (TexCoord2f)inList.get(i);
+ }
+ return outList;
+ } // End of objectToTexCoord2Array
+
+
+ private Vector3f[] objectToVectorArray(ArrayList inList) {
+ Vector3f outList[] = new Vector3f[inList.size()];
+ for (int i = 0 ; i < inList.size() ; i++) {
+ outList[i] = (Vector3f)inList.get(i);
+ }
+ return outList;
+ } // End of objectToVectorArray
+
+
+ /**
+ * Each group is a list of indices into the model's index lists,
+ * indicating the starting index of each triangle in the group.
+ * This method converts those data structures
+ * into an integer array to use with GeometryInfo.
+ */
+ private int[] groupIndices(ArrayList sourceList, ArrayList group) {
+ int indices[] = new int[group.size() * 3];
+ for (int i = 0 ; i < group.size() ; i++) {
+ int j = ((Integer)group.get(i)).intValue();
+ indices[i * 3 + 0] = ((Integer)sourceList.get(j + 0)).intValue();
+ indices[i * 3 + 1] = ((Integer)sourceList.get(j + 1)).intValue();
+ indices[i * 3 + 2] = ((Integer)sourceList.get(j + 2)).intValue();
+ }
+ return indices;
+ } // end of groupIndices
+
+
+ /**
+ * smoothingGroupNormals
+ *
+ * Smoothing groups are groups of faces who should be grouped
+ * together for normal calculation purposes. The faces are
+ * put into a GeometryInfo object and normals are calculated
+ * with a 180 degree creaseAngle (no creases) or whatever the
+ * user has specified. The normals
+ * are then copied out of the GeometryInfo and back into
+ * ObjectFile data structures.
+ */
+ private void smoothingGroupNormals() {
+ NormalGenerator ng =
+ new NormalGenerator(radians == -1.0f ? Math.PI : radians);
+ NormalGenerator ng0 = new NormalGenerator(0.0);
+ normList.clear();
+ normIdxList = null;
+ int newNormIdxArray[] = new int[coordIdxList.size()];
+
+ Iterator e = triSgroups.keySet().iterator();
+ while (e.hasNext()) {
+ String curname = (String)e.next();
+ ArrayList triList = (ArrayList)triSgroups.get(curname);
+
+ // Check for group with no faces
+ if (triList.size() > 0) {
+
+ GeometryInfo gi = new GeometryInfo(GeometryInfo.TRIANGLE_ARRAY);
+
+ gi.setCoordinateIndices(groupIndices(coordIdxList, triList));
+ gi.setCoordinates(coordArray);
+
+ if (curname.equals("0")) ng0.generateNormals(gi);
+ else ng.generateNormals(gi);
+
+ // Get the generated normals and indices
+ Vector3f genNorms[] = gi.getNormals();
+ int genNormIndices[] = gi.getNormalIndices();
+
+ // Now we need to copy the generated normals into ObjectFile
+ // data structures (normList and normIdxList). The variable
+ // normIdx is the index of the index of the normal currently
+ // being put into the list. It takes some calculation to
+ // figure out the new index and where to put it.
+ int normIdx = 0;
+ // Repeat for each triangle in the smoothing group
+ for (int i = 0 ; i < triList.size() ; i++) {
+
+ // Get the coordIdxList index of the first index in this face
+ int idx = ((Integer)triList.get(i)).intValue();
+
+ // Repeat for each vertex in the triangle
+ for (int j = 0 ; j < 3 ; j++) {
+
+ // Put the new normal's index into the index list
+ newNormIdxArray[idx + j] = normList.size();
+
+ // Add the vertex's normal to the normal list
+ normList.add(genNorms[genNormIndices[normIdx++]]);
+ }
+ }
+ }
+ }
+ normIdxList = new ArrayList(coordIdxList.size());
+ for (int i = 0 ; i < coordIdxList.size() ; i++) {
+ normIdxList.add(new Integer(newNormIdxArray[i]));
+ }
+ normArray = objectToVectorArray(normList);
+ } // end of smoothingGroupNormals
+
+
+ /**
+ * Each face is converted to triangles. As each face is converted,
+ * we look up which geometry group and smoothing group the face
+ * belongs to. The generated triangles are added to each of these
+ * groups, which are also being converted to a new triangle based format.
+ *
+ * We need to convert to triangles before normals are generated
+ * because of smoothing groups. The faces in a smoothing group
+ * are copied into a GeometryInfo to have their normals calculated,
+ * and then the normals are copied out of the GeometryInfo using
+ * GeometryInfo.getNormalIndices. As part of Normal generation,
+ * the geometry gets converted to Triangles. So we need to convert
+ * to triangles *before* Normal generation so that the normals we
+ * read out of the GeometryInfo match up with the vertex data
+ * that we sent in. If we sent in TRIANGLE_FAN data, the normal
+ * generator would convert it to triangles and we'd read out
+ * normals formatted for Triangle data. This would not match up
+ * with our original Fan data, so we couldn't tell which normals
+ * go with which vertices.
+ */
+ private void convertToTriangles() {
+ boolean triangulate = (flags & TRIANGULATE) != 0;
+ boolean textures = !texList.isEmpty() && !texIdxList.isEmpty() &&
+ (texIdxList.size() == coordIdxList.size());
+ boolean normals = !normList.isEmpty() && !normIdxList.isEmpty() &&
+ (normIdxList.size() == coordIdxList.size());
+ int numFaces = stripCounts.size();
+ boolean haveSgroups = curSgroup != null;
+
+ triGroups = new HashMap(50);
+ if (haveSgroups) triSgroups = new HashMap(50);
+
+ ArrayList newCoordIdxList = null;
+ ArrayList newTexIdxList = null;
+ ArrayList newNormIdxList = null;
+
+ if (triangulate) {
+ GeometryInfo gi = new GeometryInfo(GeometryInfo.POLYGON_ARRAY);
+ gi.setStripCounts(objectToIntArray(stripCounts));
+ gi.setCoordinates(coordArray);
+ gi.setCoordinateIndices(objectToIntArray(coordIdxList));
+ if (textures) {
+ gi.setTextureCoordinateParams(1, 2);
+ gi.setTextureCoordinates(0, texArray);
+ gi.setTextureCoordinateIndices(0, objectToIntArray(texIdxList));
+ }
+ if (normals) {
+ gi.setNormals(normArray);
+ gi.setNormalIndices(objectToIntArray(normIdxList));
+ }
+ gi.convertToIndexedTriangles();
+
+ // Data is now indexed triangles. Next step is to take the data
+ // out of the GeometryInfo and put into internal data structures
+
+ int coordIndicesArray[] = gi.getCoordinateIndices();
+
+ // Fix for #4366060
+ // Make sure triangulated geometry has the correct number of triangles
+ int tris = 0;
+ for (int i = 0 ; i < numFaces ; i++)
+ tris += ((Integer)stripCounts.get(i)).intValue() - 2;
+
+ if (coordIndicesArray.length != (tris * 3)) {
+ // Model contains bad polygons that didn't triangulate into the
+ // correct number of triangles. Fall back to "simple" triangulation
+ triangulate = false;
+ } else {
+
+ int texIndicesArray[] = gi.getTextureCoordinateIndices();
+ int normIndicesArray[] = gi.getNormalIndices();
+
+ // Convert index arrays to internal ArrayList format
+ coordIdxList.clear();
+ texIdxList.clear();
+ normIdxList.clear();
+ for (int i = 0 ; i < coordIndicesArray.length ; i++) {
+ coordIdxList.add(new Integer(coordIndicesArray[i]));
+ if (textures) texIdxList.add(new Integer(texIndicesArray[i]));
+ if (normals) normIdxList.add(new Integer(normIndicesArray[i]));
+ }
+ }
+ }
+
+ if (!triangulate) {
+ newCoordIdxList = new ArrayList();
+ if (textures) newTexIdxList = new ArrayList();
+ if (normals) newNormIdxList = new ArrayList();
+ }
+
+ // Repeat for each face in the model - add the triangles from each
+ // face to the Geometry and Smoothing Groups
+ int baseVertex = 0;
+ for (int f = 0 ; f < numFaces ; f++) {
+ int faceSize = ((Integer)stripCounts.get(f)).intValue();
+
+ // Find out the name of the group to which this face belongs
+ Integer curFace = new Integer(f);
+ curGroup = (String)groups.get(curFace);
+
+ // Change to a new geometry group, create if it doesn't exist
+ curTriGroup = (ArrayList)triGroups.get(curGroup);
+ if (curTriGroup == null) {
+ curTriGroup = new ArrayList();
+ triGroups.put(curGroup, curTriGroup);
+ }
+
+ // Change to a new smoothing group, create if it doesn't exist
+ if (haveSgroups) {
+ curSgroup = (String)sGroups.get(curFace);
+ if (curSgroup == null) {
+ // Weird case - this face has no smoothing group. Happens if the
+ // first 's' token comes after some faces have already been defined.
+ // Assume they wanted no smoothing for these faces
+ curSgroup = "0";
+ }
+ curTriSgroup = (ArrayList)triSgroups.get(curSgroup);
+ if (curTriSgroup == null) {
+ curTriSgroup = new ArrayList();
+ triSgroups.put(curSgroup, curTriSgroup);
+ }
+ }
+
+ if (triangulate) {
+
+ // Each polygon of n vertices is now n-2 triangles
+ for (int t = 0 ; t < faceSize - 2 ; t++) {
+
+ // The groups just remember the first vertex of each triangle
+ Integer triBaseVertex = new Integer(baseVertex);
+ curTriGroup.add(triBaseVertex);
+ if (haveSgroups) curTriSgroup.add(triBaseVertex);
+
+ baseVertex += 3;
+ }
+ } else {
+ // Triangulate simply
+ for (int v = 0 ; v < faceSize - 2 ; v++) {
+ // Add this triangle to the geometry group and the smoothing group
+ Integer triBaseVertex = new Integer(newCoordIdxList.size());
+ curTriGroup.add(triBaseVertex);
+ if (haveSgroups) curTriSgroup.add(triBaseVertex);
+
+ newCoordIdxList.add(coordIdxList.get(baseVertex));
+ newCoordIdxList.add(coordIdxList.get(baseVertex + v + 1));
+ newCoordIdxList.add(coordIdxList.get(baseVertex + v + 2));
+
+ if (textures) {
+ newTexIdxList.add(texIdxList.get(baseVertex));
+ newTexIdxList.add(texIdxList.get(baseVertex + v + 1));
+ newTexIdxList.add(texIdxList.get(baseVertex + v + 2));
+ }
+
+ if (normals) {
+ newNormIdxList.add(normIdxList.get(baseVertex));
+ newNormIdxList.add(normIdxList.get(baseVertex + v + 1));
+ newNormIdxList.add(normIdxList.get(baseVertex + v + 2));
+ }
+ }
+ baseVertex += faceSize;
+ }
+ }
+
+ // No need to keep these around
+ stripCounts = null;
+ groups = null;
+ sGroups = null;
+
+ if (!triangulate) {
+ coordIdxList = newCoordIdxList;
+ texIdxList = newTexIdxList;
+ normIdxList = newNormIdxList;
+ }
+ } // End of convertToTriangles
+
+
+ private SceneBase makeScene() {
+ // Create Scene to pass back
+ SceneBase scene = new SceneBase();
+ BranchGroup group = new BranchGroup();
+ scene.setSceneGroup(group);
+
+ boolean gen_norms = normList.isEmpty() || normIdxList.isEmpty() ||
+ (normIdxList.size() != coordIdxList.size());
+ boolean do_tex = !texList.isEmpty() && !texIdxList.isEmpty() &&
+ (texIdxList.size() == coordIdxList.size());
+
+ // Convert ArrayLists to arrays
+ coordArray = objectToPoint3Array(coordList);
+ if (!gen_norms) normArray = objectToVectorArray(normList);
+ if (do_tex) texArray = objectToTexCoord2Array(texList);
+
+ convertToTriangles();
+
+ if ((DEBUG & 8) != 0) {
+ time = System.currentTimeMillis() - time;
+ System.out.println("Convert to triangles: " + time + " ms");
+ time = System.currentTimeMillis();
+ }
+
+ if ((gen_norms) && (curSgroup != null)) {
+ smoothingGroupNormals();
+ gen_norms = false;
+ if ((DEBUG & 8) != 0) {
+ time = System.currentTimeMillis() - time;
+ System.out.println("Smoothing group normals: " + time + " ms");
+ time = System.currentTimeMillis();
+ }
+ }
+
+ NormalGenerator ng = null;
+ if (gen_norms) ng = new NormalGenerator(radians);
+
+ Stripifier strippy = null;
+ if ((flags & STRIPIFY) != 0) strippy = new Stripifier();
+
+ long t1 = 0, t2 = 0, t3 = 0, t4 = 0;
+
+ // Each "Group" of faces in the model will be one Shape3D
+ Iterator e = triGroups.keySet().iterator();
+ while (e.hasNext()) {
+
+ String curname = (String)e.next();
+ ArrayList triList = (ArrayList)triGroups.get(curname);
+
+ // Check for group with no faces
+ if (triList.size() > 0) {
+
+ GeometryInfo gi = new GeometryInfo(GeometryInfo.TRIANGLE_ARRAY);
+
+ gi.setCoordinateIndices(groupIndices(coordIdxList, triList));
+ gi.setCoordinates(coordArray);
+
+ if (do_tex) {
+ gi.setTextureCoordinateParams(1, 2);
+ gi.setTextureCoordinates(0, texArray);
+ gi.setTextureCoordinateIndices(0, groupIndices(texIdxList, triList));
+ }
+
+ if ((DEBUG & 8) != 0) time = System.currentTimeMillis();
+ if (gen_norms) {
+ if ((flags & REVERSE) != 0) gi.reverse();
+ ng.generateNormals(gi);
+ if ((DEBUG & 8) != 0) {
+ t2 += System.currentTimeMillis() - time;
+ System.out.println("Generate normals: " + t2 + " ms");
+ time = System.currentTimeMillis();
+ }
+ } else {
+ gi.setNormalIndices(groupIndices(normIdxList, triList));
+ gi.setNormals(normArray);
+ if ((flags & REVERSE) != 0) gi.reverse();
+ }
+
+ if ((flags & STRIPIFY) != 0) {
+ strippy.stripify(gi);
+ if ((DEBUG & 8) != 0) {
+ t3 += System.currentTimeMillis() - time;
+ System.out.println("Stripify: " + t3 + " ms");
+ time = System.currentTimeMillis();
+ }
+ }
+
+ // Put geometry into Shape3d
+ Shape3D shape = new Shape3D();
+
+ shape.setGeometry(gi.getGeometryArray(true, true, false));
+
+ String matName = (String)groupMaterials.get(curname);
+ materials.assignMaterial(matName, shape);
+
+ group.addChild(shape);
+ scene.addNamedObject(curname, shape);
+
+ if ((DEBUG & 8) != 0) {
+ t4 += System.currentTimeMillis() - time;
+ System.out.println("Shape 3D: " + t4 + " ms");
+ time = System.currentTimeMillis();
+ }
+ }
+ }
+
+ return scene;
+ } // end of makeScene
+
+
+ /**
+ * The Object File is loaded from the already opened file.
+ * To attach the model to your scene, call getSceneGroup() on
+ * the Scene object passed back, and attach the returned
+ * BranchGroup to your scene graph. For an example, see
+ * j3d-examples/ObjLoad/ObjLoad.java.
+ */
+ public Scene load(Reader reader) throws FileNotFoundException,
+ IncorrectFormatException,
+ ParsingErrorException {
+ // ObjectFileParser does lexical analysis
+ ObjectFileParser st = new ObjectFileParser(reader);
+
+ coordList = new ArrayList();
+ texList = new ArrayList();
+ normList = new ArrayList();
+ coordIdxList = new ArrayList();
+ texIdxList = new ArrayList();
+ normIdxList = new ArrayList();
+ groups = new HashMap(50);
+ curGroup = "default";
+ sGroups = new HashMap(50);
+ curSgroup = null;
+ stripCounts = new ArrayList();
+ groupMaterials = new HashMap(50);
+ groupMaterials.put(curGroup, "default");
+ materials = new ObjectFileMaterials();
+
+ time = 0L;
+ if ((DEBUG & 8) != 0) {
+ time = System.currentTimeMillis();
+ }
+
+ readFile(st);
+
+ if ((DEBUG & 8) != 0) {
+ time = System.currentTimeMillis() - time;
+ System.out.println("Read file: " + time + " ms");
+ time = System.currentTimeMillis();
+ }
+
+ if ((flags & RESIZE) != 0) resize();
+
+ return makeScene();
+ } // End of load(Reader)
+
+
+ /**
+ * For an .obj file loaded from a URL, set the URL where associated files
+ * (like material properties files) will be found.
+ * Only needs to be called to set it to a different URL
+ * from that containing the .obj file.
+ */
+ public void setBaseUrl(URL url) {
+ baseUrl = url;
+ } // End of setBaseUrl
+
+
+ /**
+ * Return the URL where files associated with this .obj file (like
+ * material properties files) will be found.
+ */
+ public URL getBaseUrl() {
+ return baseUrl;
+ } // End of getBaseUrl
+
+
+ /**
+ * Set the path where files associated with this .obj file are
+ * located.
+ * Only needs to be called to set it to a different directory
+ * from that containing the .obj file.
+ */
+ public void setBasePath(String pathName) {
+ basePath = pathName;
+ if (basePath == null || basePath == "")
+ basePath = "." + java.io.File.separator;
+ basePath = basePath.replace('/', java.io.File.separatorChar);
+ basePath = basePath.replace('\\', java.io.File.separatorChar);
+ if (!basePath.endsWith(java.io.File.separator))
+ basePath = basePath + java.io.File.separator;
+ } // End of setBasePath
+
+
+ /**
+ * Return the path where files associated with this .obj file (like material
+ * files) are located.
+ */
+ public String getBasePath() {
+ return basePath;
+ } // End of getBasePath
+
+
+ /**
+ * Set parameters for loading the model.
+ * Flags defined in Loader.java are ignored by the ObjectFile Loader
+ * because the .obj file format doesn't include lights, fog, background,
+ * behaviors, views, or sounds. However, several flags are defined
+ * specifically for use with the ObjectFile Loader (see above).
+ */
+ public void setFlags(int flags) {
+ this.flags = flags;
+ if ((DEBUG & 4) != 0) System.out.println("Flags = " + flags);
+ } // End of setFlags
+
+
+ /**
+ * Get the parameters currently defined for loading the model.
+ * Flags defined in Loader.java are ignored by the ObjectFile Loader
+ * because the .obj file format doesn't include lights, fog, background,
+ * behaviors, views, or sounds. However, several flags are defined
+ * specifically for use with the ObjectFile Loader (see above).
+ */
+ public int getFlags() {
+ return flags;
+ } // End of getFlags
+
+} // End of class ObjectFile
+
+// End of file ObjectFile.java
diff --git a/src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFileMaterials.java b/src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFileMaterials.java
new file mode 100644
index 0000000..8dd3b2c
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFileMaterials.java
@@ -0,0 +1,425 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.objectfile;
+
+import javax.media.j3d.Appearance;
+import javax.media.j3d.Material;
+import javax.media.j3d.Shape3D;
+import javax.vecmath.Color3f;
+import com.sun.j3d.loaders.ParsingErrorException;
+import com.sun.j3d.loaders.IncorrectFormatException;
+import java.io.FileNotFoundException;
+import java.io.StringReader;
+import java.io.Reader;
+import java.io.FileReader;
+import java.io.BufferedReader;
+import java.io.BufferedInputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.FileInputStream;
+import java.util.HashMap;
+import com.sun.j3d.loaders.objectfile.DefaultMaterials;
+import java.net.URL;
+import java.net.MalformedURLException;
+import java.awt.Toolkit;
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import javax.media.j3d.Texture2D;
+import java.awt.image.ImageObserver;
+import java.awt.image.PixelGrabber;
+import java.awt.image.DataBufferInt;
+import javax.media.j3d.ImageComponent2D;
+import javax.media.j3d.TexCoordGeneration;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import javax.media.j3d.GeometryArray;
+import com.sun.j3d.utils.image.TextureLoader;
+import javax.media.j3d.TransparencyAttributes;
+
+
+class ObjectFileMaterials implements ImageObserver {
+ // DEBUG
+ // 1 = Name of materials
+ // 16 = Tokens
+ private static final int DEBUG = 0;
+
+ private String curName = null;
+ private ObjectFileMaterial cur = null;
+
+ private HashMap materials; // key=String name of material
+ // value=ObjectFileMaterial
+
+ private String basePath;
+ private boolean fromUrl;
+
+
+ private class ObjectFileMaterial {
+
+ public Color3f Ka;
+ public Color3f Kd;
+ public Color3f Ks;
+ public int illum;
+ public float Ns;
+ public Texture2D t;
+ public boolean transparent;
+
+ public void ObjectFileMaterial() {
+ Ka = null;
+ Kd = null;
+ Ks = null;
+ illum = -1;
+ Ns = -1.0f;
+ t = null;
+ } // End of ObjectFileMaterial
+ }
+
+
+ void assignMaterial(String matName, Shape3D shape) {
+ ObjectFileMaterial p = null;
+
+ if ((DEBUG & 1) != 0) System.out.println("Color " + matName);
+
+ Material m = new Material();
+ p = (ObjectFileMaterial)materials.get(matName);
+ Appearance a = new Appearance();
+
+ if (p != null) {
+ // Set ambient & diffuse color
+ if (p.Ka != null) m.setAmbientColor(p.Ka);
+ if (p.Kd != null) m.setDiffuseColor(p.Kd);
+
+ // Set specular color
+ if ((p.Ks != null) && (p.illum != 1)) m.setSpecularColor(p.Ks);
+ else if (p.illum == 1) m.setSpecularColor(0.0f, 0.0f, 0.0f);
+
+ if (p.illum >= 1) m.setLightingEnable(true);
+ else if (p.illum == 0) m.setLightingEnable(false);
+
+ if (p.Ns != -1.0f) m.setShininess(p.Ns);
+
+ if (p.t != null) {
+ a.setTexture(p.t);
+ // Create Texture Coordinates if not already present
+ if ((((GeometryArray)shape.getGeometry()).getVertexFormat() &
+ GeometryArray.TEXTURE_COORDINATE_2) == 0) {
+ TexCoordGeneration tcg = new TexCoordGeneration();
+ a.setTexCoordGeneration(tcg);
+ }
+ }
+
+ if (p.transparent)
+ a.setTransparencyAttributes(
+ new TransparencyAttributes(TransparencyAttributes.NICEST,
+ 0.0f));
+ }
+ a.setMaterial(m);
+ if ((DEBUG & 1) != 0) System.out.println(m);
+ shape.setAppearance(a);
+ } // End of assignMaterial
+
+
+ private void readName(ObjectFileParser st) throws ParsingErrorException {
+ st.getToken();
+
+ if (st.ttype == ObjectFileParser.TT_WORD) {
+
+ if (curName != null) materials.put(curName, cur);
+ curName = new String(st.sval);
+ cur = new ObjectFileMaterial();
+ }
+ st.skipToNextLine();
+ } // End of readName
+
+
+ private void readAmbient(ObjectFileParser st) throws ParsingErrorException {
+ Color3f p = new Color3f();
+
+ st.getNumber();
+ p.x = (float)st.nval;
+ st.getNumber();
+ p.y = (float)st.nval;
+ st.getNumber();
+ p.z = (float)st.nval;
+
+ cur.Ka = p;
+
+ st.skipToNextLine();
+ } // End of readAmbient
+
+
+ private void readDiffuse(ObjectFileParser st) throws ParsingErrorException {
+ Color3f p = new Color3f();
+
+ st.getNumber();
+ p.x = (float)st.nval;
+ st.getNumber();
+ p.y = (float)st.nval;
+ st.getNumber();
+ p.z = (float)st.nval;
+
+ cur.Kd = p;
+
+ st.skipToNextLine();
+ } // End of readDiffuse
+
+
+ private void readSpecular(ObjectFileParser st) throws ParsingErrorException {
+ Color3f p = new Color3f();
+
+ st.getNumber();
+ p.x = (float)st.nval;
+ st.getNumber();
+ p.y = (float)st.nval;
+ st.getNumber();
+ p.z = (float)st.nval;
+
+ cur.Ks = p;
+
+ st.skipToNextLine();
+ } // End of readSpecular
+
+
+ private void readIllum(ObjectFileParser st) throws ParsingErrorException {
+ float f;
+
+ st.getNumber();
+ cur.illum = (int)st.nval;
+
+ st.skipToNextLine();
+ } // End of readSpecular
+
+
+ private void readShininess(ObjectFileParser st) throws ParsingErrorException {
+ float f;
+
+ st.getNumber();
+ cur.Ns = (float)st.nval;
+ if (cur.Ns < 1.0f) cur.Ns = 1.0f;
+ else if (cur.Ns > 128.0f) cur.Ns = 128.0f;
+
+ st.skipToNextLine();
+ } // End of readSpecular
+
+
+ public void readMapKd(ObjectFileParser st) {
+ // Filenames are case sensitive
+ st.lowerCaseMode(false);
+
+ // Get name of texture file (skip path)
+ String tFile = null;
+ do {
+ st.getToken();
+ if (st.ttype == ObjectFileParser.TT_WORD) tFile = st.sval;
+ } while (st.ttype != ObjectFileParser.TT_EOL);
+
+ st.lowerCaseMode(true);
+
+ if (tFile != null) {
+ // Check for filename with no extension
+ if (tFile.lastIndexOf('.') != -1) {
+ try {
+ // Convert filename to lower case for extension comparisons
+ String suffix =
+ tFile.substring(tFile.lastIndexOf('.') + 1).toLowerCase();
+
+ TextureLoader t = null;
+
+ if ((suffix.equals("int")) || (suffix.equals("inta")) ||
+ (suffix.equals("rgb")) || (suffix.equals("rgba")) ||
+ (suffix.equals("bw")) || (suffix.equals("sgi"))) {
+ RgbFile f;
+ if (fromUrl) {
+ f = new RgbFile(new URL(basePath + tFile).openStream());
+ } else {
+ f = new RgbFile(new FileInputStream(basePath + tFile));
+ }
+ BufferedImage bi = f.getImage();
+
+ boolean luminance = suffix.equals("int") || suffix.equals("inta");
+ boolean alpha = suffix.equals("inta") || suffix.equals("rgba");
+ cur.transparent = alpha;
+
+ String s = null;
+ if (luminance && alpha) s = "LUM8_ALPHA8";
+ else if (luminance) s = "LUMINANCE";
+ else if (alpha) s = "RGBA";
+ else s = "RGB";
+
+ t = new TextureLoader(bi, s, TextureLoader.GENERATE_MIPMAP);
+ } else {
+ // For all other file types, use the TextureLoader
+ if (fromUrl) {
+ t = new TextureLoader(new URL(basePath + tFile), "RGB",
+ TextureLoader.GENERATE_MIPMAP, null);
+ } else {
+ t = new TextureLoader(basePath + tFile, "RGB",
+ TextureLoader.GENERATE_MIPMAP, null);
+ }
+ }
+ Texture2D texture = (Texture2D)t.getTexture();
+ if (texture != null) cur.t = texture;
+ }
+ catch (FileNotFoundException e) {
+ // Texture won't get loaded if file can't be found
+ }
+ catch (MalformedURLException e) {
+ // Texture won't get loaded if file can't be found
+ }
+ catch (IOException e) {
+ // Texture won't get loaded if file can't be found
+ }
+ }
+ }
+ st.skipToNextLine();
+ } // End of readMapKd
+
+
+ private void readFile(ObjectFileParser st) throws ParsingErrorException {
+ int t;
+ st.getToken();
+ while (st.ttype != ObjectFileParser.TT_EOF) {
+
+ // Print out one token for each line
+ if ((DEBUG & 16) != 0) {
+ System.out.print("Token ");
+ if (st.ttype == ObjectFileParser.TT_EOL) System.out.println("EOL");
+ else if (st.ttype == ObjectFileParser.TT_WORD)
+ System.out.println(st.sval);
+ else System.out.println((char)st.ttype);
+ }
+
+ if (st.ttype == ObjectFileParser.TT_WORD) {
+ if (st.sval.equals("newmtl")) {
+ readName(st);
+ } else if (st.sval.equals("ka")) {
+ readAmbient(st);
+ } else if (st.sval.equals("kd")) {
+ readDiffuse(st);
+ } else if (st.sval.equals("ks")) {
+ readSpecular(st);
+ } else if (st.sval.equals("illum")) {
+ readIllum(st);
+ } else if (st.sval.equals("d")) {
+ st.skipToNextLine();
+ } else if (st.sval.equals("ns")) {
+ readShininess(st);
+ } else if (st.sval.equals("tf")) {
+ st.skipToNextLine();
+ } else if (st.sval.equals("sharpness")) {
+ st.skipToNextLine();
+ } else if (st.sval.equals("map_kd")) {
+ readMapKd(st);
+ } else if (st.sval.equals("map_ka")) {
+ st.skipToNextLine();
+ } else if (st.sval.equals("map_ks")) {
+ st.skipToNextLine();
+ } else if (st.sval.equals("map_ns")) {
+ st.skipToNextLine();
+ } else if (st.sval.equals("bump")) {
+ st.skipToNextLine();
+ }
+ }
+
+ st.skipToNextLine();
+
+ // Get next token
+ st.getToken();
+ }
+ if (curName != null) materials.put(curName, cur);
+ } // End of readFile
+
+
+ void readMaterialFile(boolean fromUrl, String basePath, String fileName)
+ throws ParsingErrorException {
+
+ Reader reader;
+
+ this.basePath = basePath;
+ this.fromUrl = fromUrl;
+
+ try {
+ if (fromUrl) {
+ reader = (Reader)
+ (new InputStreamReader(
+ new BufferedInputStream(
+ (new URL(basePath + fileName).openStream()))));
+ } else {
+ reader = new BufferedReader(new FileReader(basePath + fileName));
+ }
+ }
+ catch (IOException e) {
+ // couldn't find it - ignore mtllib
+ return;
+ }
+ if ((DEBUG & 1) != 0)
+ System.out.println("Material file: " + basePath + fileName);
+
+ ObjectFileParser st = new ObjectFileParser(reader);
+ readFile(st);
+ } // End of readMaterialFile
+
+
+ ObjectFileMaterials() throws ParsingErrorException {
+ Reader reader = new StringReader(DefaultMaterials.materials);
+
+ ObjectFileParser st = new ObjectFileParser(reader);
+ materials = new HashMap(50);
+ readFile(st);
+ } // End of ObjectFileMaterials
+
+
+ /**
+ * Implement the ImageObserver interface. Needed to load jpeg and gif
+ * files using the Toolkit.
+ */
+ public boolean imageUpdate(Image img, int flags,
+ int x, int y, int w, int h) {
+
+ return (flags & (ALLBITS | ABORT)) == 0;
+ } // End of imageUpdate
+
+} // End of class ObjectFileMaterials
+
+// End of file ObjectFileMaterials.java
diff --git a/src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFileParser.java b/src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFileParser.java
new file mode 100644
index 0000000..ded314a
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFileParser.java
@@ -0,0 +1,179 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.objectfile;
+
+import java.io.StreamTokenizer;
+import java.io.IOException;
+import java.io.Reader;
+import com.sun.j3d.loaders.ParsingErrorException;
+
+class ObjectFileParser extends StreamTokenizer {
+
+ private static final char BACKSLASH = '\\';
+
+
+ /**
+ * setup
+ *
+ * Sets up StreamTokenizer for reading ViewPoint .obj file format.
+ */
+ void setup() {
+ resetSyntax();
+ eolIsSignificant(true);
+ lowerCaseMode(true);
+
+ // All printable ascii characters
+ wordChars('!', '~');
+
+ // Comment from ! to end of line
+ commentChar('!');
+
+ whitespaceChars(' ', ' ');
+ whitespaceChars('\n', '\n');
+ whitespaceChars('\r', '\r');
+ whitespaceChars('\t', '\t');
+
+ // These characters returned as tokens
+ ordinaryChar('#');
+ ordinaryChar('/');
+ ordinaryChar(BACKSLASH);
+ } // End of setup
+
+
+ /**
+ * getToken
+ *
+ * Gets the next token from the stream. Puts one of the four
+ * constants (TT_WORD, TT_NUMBER, TT_EOL, or TT_EOF) or the token value
+ * for single character tokens into ttype. Handles backslash
+ * continuation of lines.
+ */
+ void getToken() throws ParsingErrorException {
+ int t;
+ boolean done = false;
+
+ try {
+ do {
+ t = nextToken();
+ if (t == BACKSLASH) {
+ t = nextToken();
+ if (ttype != TT_EOL) done = true;
+ } else done = true;
+ } while (!done);
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(
+ "IO error on line " + lineno() + ": " + e.getMessage());
+ }
+ } // End of getToken
+
+
+ void printToken() {
+ switch (ttype) {
+ case TT_EOL:
+ System.out.println("Token EOL");
+ break;
+ case TT_EOF:
+ System.out.println("Token EOF");
+ break;
+ case TT_WORD:
+ System.out.println("Token TT_WORD: " + sval);
+ break;
+ case '/':
+ System.out.println("Token /");
+ break;
+ case BACKSLASH:
+ System.out.println("Token " + BACKSLASH);
+ break;
+ case '#':
+ System.out.println("Token #");
+ break;
+ }
+ } // end of printToken
+
+
+ /**
+ * skipToNextLine
+ *
+ * Skips all tokens on the rest of this line. Doesn't do anything if
+ * We're already at the end of a line
+ */
+ void skipToNextLine() throws ParsingErrorException {
+ while (ttype != TT_EOL) {
+ getToken();
+ }
+ } // end of skipToNextLine
+
+
+ /**
+ * getNumber
+ *
+ * Gets a number from the stream. Note that we don't recognize
+ * numbers in the tokenizer automatically because numbers might be in
+ * scientific notation, which isn't processed correctly by
+ * StreamTokenizer. The number is returned in nval.
+ */
+ void getNumber() throws ParsingErrorException {
+ int t;
+
+ try {
+ getToken();
+ if (ttype != TT_WORD)
+ throw new ParsingErrorException("Expected number on line " + lineno());
+ nval = (Double.valueOf(sval)).doubleValue();
+ }
+ catch (NumberFormatException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ } // end of getNumber
+
+
+ // ObjectFileParser constructor
+ ObjectFileParser(Reader r) {
+ super(r);
+ setup();
+ } // end of ObjectFileParser
+
+} // End of file ObjectFileParser.java
diff --git a/src/classes/share/com/sun/j3d/loaders/objectfile/RgbFile.java b/src/classes/share/com/sun/j3d/loaders/objectfile/RgbFile.java
new file mode 100644
index 0000000..5512ec2
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/objectfile/RgbFile.java
@@ -0,0 +1,202 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.objectfile;
+
+import java.io.BufferedInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.WritableRaster;
+import java.awt.color.ColorSpace;
+import java.awt.Transparency;
+
+class RgbFile extends BufferedInputStream {
+
+ // Header data
+ short dimension;
+ short xSize;
+ short ySize;
+ short zSize;
+
+ String filename;
+
+ private static final int DEBUG = 0;
+
+
+ short getShort() throws IOException {
+ int t1 = (short)read();
+ if (t1 == -1) throw new IOException("Unexpected EOF");
+ int t2 = (short)read();
+ if (t2 == -1) throw new IOException("Unexpected EOF");
+ return (short)((t1 << 8) | t2);
+ } // End of getShort()
+
+
+ byte getByte() throws IOException {
+ int t = read();
+ if (t == -1) throw new IOException("Unexpected EOF");
+ return (byte)t;
+ } // End of getByte
+
+
+ int getInt() throws IOException {
+ int ret = 0;
+ for (int i = 0 ; i < 4 ; i++) {
+ int t = read();
+ if (t == -1) throw new IOException("Unexpected EOF");
+ ret = (ret << 8) | t;
+ }
+ return ret;
+ } // end of getInt
+
+
+ public BufferedImage getImage() throws IOException {
+ short magic = getShort();
+
+ if (magic != 474) throw new IOException("Unrecognized file format.");
+
+ byte storage = getByte();
+
+ if (storage != 0)
+ throw new IOException("RLE Compressed files not supported");
+
+ byte bpc = getByte();
+ dimension = getShort();
+ xSize = getShort();
+ ySize = getShort();
+ zSize = getShort();
+ int pixMin = getInt();
+ int pixMax = getInt();
+ skip(84l);
+ int colorMap = getInt();
+
+ if ((DEBUG & 1) != 0) {
+ System.out.println(filename + ":");
+ System.out.println(" bpc = " + bpc);
+ System.out.println(" dimension = " + dimension);
+ System.out.println(" xSize = " + xSize);
+ System.out.println(" ySize = " + ySize);
+ System.out.println(" zSize = " + zSize);
+ System.out.println(" pixMin = " + pixMin);
+ System.out.println(" pixMax = " + pixMax);
+ System.out.println(" colorMap = " + colorMap);
+ }
+
+ if ((pixMin != 0) || (pixMax != 0xff) || (colorMap != 0) || (bpc != 1))
+ throw new IOException("Unsupported options in file");
+
+ skip(404l);
+
+ ComponentColorModel cm = null;
+ if (zSize == 1) {
+ // Black and White image
+ ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
+
+ int[] nBits = {8};
+ cm = new ComponentColorModel(cs, nBits, false, false,
+ Transparency.OPAQUE,
+ DataBuffer.TYPE_BYTE);
+
+ } else if (zSize == 2) {
+ // Black and White image with alpha
+ ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
+
+ int[] nBits = {8, 8};
+ cm = new ComponentColorModel(cs, nBits, true, false,
+ Transparency.TRANSLUCENT,
+ DataBuffer.TYPE_BYTE);
+
+ } else if (zSize == 3) {
+ // RGB Image
+ ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+
+ int[] nBits = {8, 8, 8};
+ cm = new ComponentColorModel(cs, nBits, false, false,
+ Transparency.OPAQUE,
+ DataBuffer.TYPE_BYTE);
+
+ } else if (zSize == 4) {
+ // RGBA Image
+ ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+
+ int[] nBits = {8, 8, 8, 8};
+ cm = new ComponentColorModel(cs, nBits, true, false,
+ Transparency.TRANSLUCENT,
+ DataBuffer.TYPE_BYTE);
+ } else {
+ throw new IOException("Unsupported options in file");
+ }
+
+ WritableRaster r = cm.createCompatibleWritableRaster(xSize, ySize);
+ BufferedImage bi = new BufferedImage(cm, r, false, null);
+
+ int t;
+ byte image[] = ((DataBufferByte)r.getDataBuffer()).getData();
+ for (short z = 0 ; z < zSize ; z++) {
+ for (int y = ySize - 1 ; y >= 0 ; y--) {
+ for (short x = 0 ; x < xSize ; x++) {
+ t = read();
+ if (t == -1) throw new IOException("Unexpected EOF");
+ image[y * (xSize * zSize) + (x * zSize) + z] = (byte)t;
+ }
+ }
+ }
+
+ return bi;
+ } // End of getImage
+
+
+ public RgbFile(InputStream s) {
+ super(s);
+ } // End of RgbFile(URL)
+
+} // End of class RgbFile
+
+// End of file RgbFile.java
diff --git a/src/classes/share/com/sun/j3d/utils/applet/JMainFrame.java b/src/classes/share/com/sun/j3d/utils/applet/JMainFrame.java
new file mode 100644
index 0000000..b4a99e1
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/applet/JMainFrame.java
@@ -0,0 +1,347 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// JMainFrame - run an Applet as an application
+//
+// Copyright (C) 1996 by Jef Poskanzer <[email protected]>. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+//
+// Visit the ACME Labs Java page for up-to-date versions of this and other
+// fine Java utilities: http://www.acme.com/java/
+
+// ---------------------------------------------------------------------
+
+package com.sun.j3d.utils.applet;
+
+import java.applet.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.*;
+import java.io.*;
+import java.net.*;
+import javax.swing.*;
+import java.util.*;
+
+public class JMainFrame extends JFrame
+ implements Runnable, AppletStub, AppletContext {
+
+ private String[] args = null;
+ private static int instances = 0;
+ private String name;
+ private Applet applet;
+ private Label label = null;
+ private Dimension appletSize;
+
+ private static final String PARAM_PROP_PREFIX = "parameter.";
+
+ public JMainFrame(Applet applet, String[] args, int width, int height) {
+ build(applet, args, width, height);
+ }
+ public JMainFrame(Applet applet, String[] args) {
+ build(applet, args, -1, -1);
+ }
+
+ public JMainFrame(Applet applet, int width, int height) {
+ build(applet, null, width, height);
+ }
+
+ private void build(Applet applet, String[] args, int width, int height) {
+ ++instances;
+ this.applet = applet;
+ this.args = args;
+ applet.setStub( this );
+ name = applet.getClass().getName();
+ setTitle( name );
+
+ // Set up properties.
+ Properties props = System.getProperties();
+ props.put( "browser", "Acme.MainFrame" );
+ props.put( "browser.version", "11jul96" );
+ props.put( "browser.vendor", "Acme Laboratories" );
+ props.put( "browser.vendor.url", "http://www.acme.com/" );
+
+ // Turn args into parameters by way of the properties list.
+ if ( args != null )
+ parseArgs( args, props );
+
+ // If width and height are specified in the parameters, override
+ // the compiled-in values.
+ String widthStr = getParameter( "width" );
+ if ( widthStr != null )
+ width = Integer.parseInt( widthStr );
+ String heightStr = getParameter( "height" );
+ if ( heightStr != null )
+ height = Integer.parseInt( heightStr );
+
+ // Were width and height specified somewhere?
+ if ( width == -1 || height == -1 ) {
+ System.err.println( "Width and height must be specified." );
+ return;
+ }
+
+ // Lay out components.
+ Container contentPane = getContentPane();
+ contentPane.add(applet, BorderLayout.CENTER);
+
+ // Set up size.
+ pack();
+ validate();
+ appletSize = applet.getSize();
+ applet.setSize( width, height );
+ setVisible(true);
+
+ /*
+ Added WindowListener inner class to detect close events.
+ */
+ SecurityManager sm = System.getSecurityManager();
+ boolean doExit = true;
+ if (sm != null) {
+ try {
+ sm.checkExit(0);
+ } catch (SecurityException e) {
+ doExit = false;
+ }
+ }
+
+ final boolean _doExit = doExit;
+
+ // WindowListener inner class to detect close events.
+ addWindowListener(new WindowAdapter()
+ {
+ public void windowClosing(WindowEvent winEvent)
+ {
+ if (JMainFrame.this.applet != null) {
+ JMainFrame.this.applet.destroy();
+ }
+ hide();
+ try {
+ dispose();
+ } catch (IllegalStateException e) {}
+
+ if (_doExit) {
+ System.exit(0);
+ }
+
+ }
+ });
+
+ // Start a separate thread to call the applet's init() and start()
+ // methods, in case they take a long time.
+ (new Thread( this )).start();
+
+ }
+
+ // Turn command-line arguments into Applet parameters, by way of the
+ // properties list.
+ private static void parseArgs( String[] args, Properties props ) {
+ for ( int i = 0; i < args.length; ++i ) {
+ String arg = args[i];
+ int ind = arg.indexOf( '=' );
+ if ( ind == -1 )
+ props.put( PARAM_PROP_PREFIX + arg.toLowerCase(), "" );
+ else
+ props.put(
+ PARAM_PROP_PREFIX + arg.substring( 0, ind ).toLowerCase(),
+ arg.substring( ind + 1 ) );
+ }
+ }
+
+ // Methods from Runnable.
+
+ /// Separate thread to call the applet's init() and start() methods.
+ public void run() {
+ showStatus( name + " initializing..." );
+ applet.init();
+ validate();
+ showStatus( name + " starting..." );
+ applet.start();
+ validate();
+ showStatus( name + " running..." );
+ }
+
+ // Methods from AppletStub.
+
+ public boolean isActive() {
+ return true;
+ }
+
+ public URL getDocumentBase() {
+ // Returns the current directory.
+ String dir = System.getProperty( "user.dir" );
+ String urlDir = dir.replace( File.separatorChar, '/' );
+ try {
+ return new URL( "file:" + urlDir + "/");
+ }
+ catch ( MalformedURLException e ) {
+ return null;
+ }
+ }
+
+ public URL getCodeBase() {
+ // Hack: loop through each item in CLASSPATH, checking if
+ // the appropriately named .class file exists there. But
+ // this doesn't account for .zip files.
+ String path = System.getProperty( "java.class.path" );
+ Enumeration st = new StringTokenizer( path, ":" );
+ while ( st.hasMoreElements() ) {
+ String dir = (String) st.nextElement();
+ String filename = dir + File.separatorChar + name + ".class";
+ File file = new File( filename );
+ if ( file.exists() ) {
+ String urlDir = dir.replace( File.separatorChar, '/' );
+ try {
+ return new URL( "file:" + urlDir + "/" );
+ }
+ catch ( MalformedURLException e ) {
+ return null;
+ }
+ }
+ }
+ return null;
+ }
+
+ public String getParameter( String name ) {
+ // Return a parameter via the munged names in the properties list.
+ return System.getProperty( PARAM_PROP_PREFIX + name.toLowerCase() );
+ }
+
+ public void appletResize( int width, int height ) {
+ // Change the frame's size by the same amount that the applet's
+ // size is changing.
+ Dimension frameSize = getSize();
+ frameSize.width += width - appletSize.width;
+ frameSize.height += height - appletSize.height;
+ setSize( frameSize );
+ appletSize = applet.getSize();
+ }
+
+ public AppletContext getAppletContext() {
+ return this;
+ }
+
+
+ // Methods from AppletContext.
+
+ public AudioClip getAudioClip( URL url ) {
+ // This is an internal undocumented routine. However, it
+ // also provides needed functionality not otherwise available.
+ // I suspect that in a future release, JavaSoft will add an
+ // audio content handler which encapsulates this, and then
+ // we can just do a getContent just like for images.
+ return new sun.applet.AppletAudioClip( url );
+ }
+
+ public Image getImage( URL url ) {
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ try {
+ ImageProducer prod = (ImageProducer) url.getContent();
+ return tk.createImage( prod );
+ }
+ catch ( IOException e ) {
+ return null;
+ }
+ }
+
+ public Applet getApplet( String name ) {
+ // Returns this Applet or nothing.
+ if ( name.equals( this.name ) )
+ return applet;
+ return null;
+ }
+
+ public Enumeration getApplets() {
+ // Just yields this applet.
+ Vector v = new Vector();
+ v.addElement( applet );
+ return v.elements();
+ }
+
+ public void showDocument( URL url ) {
+ // Ignore.
+ }
+
+ public void showDocument( URL url, String target ) {
+ // Ignore.
+ }
+
+ public void showStatus( String status ) {
+ if ( label != null )
+ label.setText( status );
+ }
+
+ public void setStream( String key, java.io.InputStream stream ) {
+ throw new RuntimeException("Not Implemented");
+ // TODO implement setStream method
+ }
+
+ public java.io.InputStream getStream( String key ) {
+ throw new RuntimeException("Not Implemented");
+ // TODO implement getStream method
+ }
+
+ public java.util.Iterator getStreamKeys() {
+ throw new RuntimeException("Not Implemented");
+ // TODO implement getStreamKeys method
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/applet/MainFrame.java b/src/classes/share/com/sun/j3d/utils/applet/MainFrame.java
new file mode 100644
index 0000000..0d31082
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/applet/MainFrame.java
@@ -0,0 +1,397 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// MainFrame - run an Applet as an application
+//
+// Copyright (C) 1996 by Jef Poskanzer <[email protected]>. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+//
+// Visit the ACME Labs Java page for up-to-date versions of this and other
+// fine Java utilities: http://www.acme.com/java/
+
+// ---------------------------------------------------------------------
+
+package com.sun.j3d.utils.applet;
+
+import java.applet.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.*;
+import java.net.*;
+import java.io.*;
+import java.util.*;
+
+/// Run an Applet as an application.
+// <P>
+// Using this class you can add a trivial main program to any Applet
+// and run it directly, as well as from a browser or the appletviewer.
+// And unlike some versions of this concept, MainFrame implements both
+// images and sound.
+// <P>
+// Sample main program:
+// <BLOCKQUOTE><PRE>
+// public static void main( String[] args )
+// {
+// new Acme.MainFrame( new ThisApplet(), args, 400, 400 );
+// }
+// </PRE></BLOCKQUOTE>
+// The only methods you need to know about are the constructors.
+// <P>
+// You can specify Applet parameters on the command line, as name=value.
+// For instance, the equivalent of:
+// <BLOCKQUOTE><PRE>
+// &lt;PARAM NAME="pause" VALUE="200"&gt;
+// </PRE></BLOCKQUOTE>
+// would just be:
+// <BLOCKQUOTE><PRE>
+// pause=200
+// </PRE></BLOCKQUOTE>
+// You can also specify three special parameters:
+// <BLOCKQUOTE><PRE>
+// width=N Width of the Applet.
+// height=N Height of the Applet.
+// barebones=true Leave off the menu bar and status area.
+// </PRE></BLOCKQUOTE>
+// <P>
+// <A HREF="/resources/classes/Acme/MainFrame.java">Fetch the software.</A><BR>
+// <A HREF="/resources/classes/Acme.tar.Z">Fetch the entire Acme package.</A>
+
+public class MainFrame extends Frame implements
+ Runnable, AppletStub, AppletContext
+{
+
+ private String[] args = null;
+ private static int instances = 0;
+ private String name;
+ private boolean barebones = true;
+ private Applet applet;
+ private Label label = null;
+ private Dimension appletSize;
+
+ private static final String PARAM_PROP_PREFIX = "parameter.";
+
+ /// Constructor with everything specified.
+ public MainFrame(Applet applet, String[] args,
+ int width, int height) {
+ build(applet, args, width, height);
+ }
+
+ /// Constructor with no default width/height.
+ public MainFrame(Applet applet, String[] args ) {
+ build(applet, args, -1, -1);
+ }
+
+ /// Constructor with no arg parsing.
+ public MainFrame(Applet applet, int width, int height) {
+ build( applet, null, width, height );
+ }
+
+ // Internal constructor routine.
+ private void build( Applet applet, String[] args,
+ int width, int height) {
+ ++instances;
+ this.applet = applet;
+ this.args = args;
+ applet.setStub( this );
+ name = applet.getClass().getName();
+ setTitle( name );
+
+ // Set up properties.
+ Properties props = System.getProperties();
+ props.put( "browser", "Acme.MainFrame" );
+ props.put( "browser.version", "11jul96" );
+ props.put( "browser.vendor", "Acme Laboratories" );
+ props.put( "browser.vendor.url", "http://www.acme.com/" );
+
+ // Turn args into parameters by way of the properties list.
+ if ( args != null )
+ parseArgs( args, props );
+
+ // If width and height are specified in the parameters, override
+ // the compiled-in values.
+ String widthStr = getParameter( "width" );
+ if ( widthStr != null ) {
+ width = Integer.parseInt( widthStr );
+ }
+
+ String heightStr = getParameter( "height" );
+ if ( heightStr != null ) {
+ height = Integer.parseInt( heightStr );
+ }
+
+ // Were width and height specified somewhere?
+ if ((width == -1) || (height == -1)) {
+ System.err.println( "Width and height must be specified." );
+ return;
+ }
+
+ // Do we want to run bare-bones?
+ String bonesStr = getParameter( "barebones" );
+ if ((bonesStr != null) && bonesStr.equals( "true" )) {
+ barebones = true;
+ }
+
+ // Lay out components.
+ setLayout( new BorderLayout() );
+ add( "Center", applet );
+
+ // Set up size.
+ pack();
+ validate();
+ appletSize = applet.getSize();
+ applet.setSize( width, height );
+ setVisible(true);
+
+
+ /*
+ Added WindowListener inner class to detect close events.
+ */
+ SecurityManager sm = System.getSecurityManager();
+ boolean doExit = true;
+
+ if (sm != null) {
+ try {
+ sm.checkExit(0);
+ } catch (SecurityException e) {
+ doExit = false;
+ }
+ }
+
+ final boolean _doExit = doExit;
+
+ addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent winEvent) {
+ if (MainFrame.this.applet != null) {
+ MainFrame.this.applet.destroy();
+ }
+ Window w = winEvent.getWindow();
+ w.hide();
+ try {
+ w.dispose();
+ } catch (IllegalStateException e) {}
+
+ if (_doExit) {
+ System.exit(0);
+ }
+ }
+ });
+
+ // Start a separate thread to call the applet's init() and start()
+ // methods, in case they take a long time.
+ (new Thread( this )).start();
+ }
+
+ // Turn command-line arguments into Applet parameters, by way of the
+ // properties list.
+ private static void parseArgs( String[] args, Properties props) {
+ String arg;
+
+ for (int i = 0; i < args.length; ++i) {
+ arg = args[i];
+ int ind = arg.indexOf( '=' );
+ if ( ind == -1 ) {
+ props.put(PARAM_PROP_PREFIX + arg.toLowerCase(), "" );
+ } else {
+ props.put(PARAM_PROP_PREFIX + arg.substring( 0, ind ).toLowerCase(),
+ arg.substring( ind + 1 ) );
+ }
+ }
+ }
+
+ // Methods from Runnable.
+
+ /// Separate thread to call the applet's init() and start() methods.
+ public void run() {
+ showStatus( name + " initializing..." );
+ applet.init();
+ validate();
+ showStatus( name + " starting..." );
+ applet.start();
+ validate();
+ showStatus( name + " running..." );
+ }
+
+
+ // Methods from AppletStub.
+ public boolean isActive() {
+ return true;
+ }
+
+ public URL getDocumentBase() {
+ // Returns the current directory.
+ String dir = System.getProperty( "user.dir" );
+ String urlDir = dir.replace( File.separatorChar, '/' );
+ try {
+ return new URL( "file:" + urlDir + "/");
+ } catch ( MalformedURLException e ) {
+ return null;
+ }
+ }
+
+ public URL getCodeBase() {
+ // Hack: loop through each item in CLASSPATH, checking if
+ // the appropriately named .class file exists there. But
+ // this doesn't account for .zip files.
+ String path = System.getProperty( "java.class.path" );
+ Enumeration st = new StringTokenizer( path, ":" );
+ while ( st.hasMoreElements() ) {
+ String dir = (String) st.nextElement();
+ String filename = dir + File.separatorChar + name + ".class";
+ File file = new File( filename );
+ if (file.exists()) {
+ String urlDir = dir.replace( File.separatorChar, '/' );
+ try {
+ return new URL( "file:" + urlDir + "/" );
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ }
+ }
+ return null;
+ }
+
+ public String getParameter(String name) {
+ // Return a parameter via the munged names in the properties list.
+ return System.getProperty( PARAM_PROP_PREFIX + name.toLowerCase() );
+ }
+
+ public void appletResize(int width, int height) {
+ // Change the frame's size by the same amount that the applet's
+ // size is changing.
+ Dimension frameSize = getSize();
+ frameSize.width += width - appletSize.width;
+ frameSize.height += height - appletSize.height;
+ setSize( frameSize );
+ appletSize = applet.getSize();
+ }
+
+ public AppletContext getAppletContext() {
+ return this;
+ }
+
+
+ // Methods from AppletContext.
+ public AudioClip getAudioClip( URL url ) {
+ // This is an internal undocumented routine. However, it
+ // also provides needed functionality not otherwise available.
+ // I suspect that in a future release, JavaSoft will add an
+ // audio content handler which encapsulates this, and then
+ // we can just do a getContent just like for images.
+ return new sun.applet.AppletAudioClip( url );
+ }
+
+ public Image getImage( URL url ) {
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ try {
+ ImageProducer prod = (ImageProducer) url.getContent();
+ return tk.createImage( prod );
+ } catch ( IOException e ) {
+ return null;
+ }
+ }
+
+ public Applet getApplet(String name) {
+ // Returns this Applet or nothing.
+ if (name.equals( this.name )) {
+ return applet;
+ }
+ return null;
+ }
+
+ public Enumeration getApplets() {
+ // Just yields this applet.
+ Vector v = new Vector();
+ v.addElement( applet );
+ return v.elements();
+ }
+
+ public void showDocument( URL url ) {
+ // Ignore.
+ }
+
+ public void showDocument( URL url, String target ) {
+ // Ignore.
+ }
+
+ public void showStatus( String status ) {
+ if (label != null) {
+ label.setText(status);
+ }
+ }
+
+ public void setStream( String key, java.io.InputStream stream ) {
+ throw new RuntimeException("Not Implemented");
+ // TODO implement setStream method
+ }
+
+ public java.io.InputStream getStream( String key ) {
+ throw new RuntimeException("Not Implemented");
+ // TODO implement getStream method
+ }
+
+ public java.util.Iterator getStreamKeys() {
+ throw new RuntimeException("Not Implemented");
+ // TODO implement getStreamKeys method
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/audio/DistanceAttenuation.java b/src/classes/share/com/sun/j3d/utils/audio/DistanceAttenuation.java
new file mode 100644
index 0000000..01821df
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/audio/DistanceAttenuation.java
@@ -0,0 +1,134 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+ /*
+ * Sound Distance Attenuation utilities
+ *
+ * Various methods to create PointSound and ConeSound distance attenuation
+ * arrays.
+ */
+
+package com.sun.j3d.utils.audio;
+
+import java.io.* ;
+import javax.vecmath.* ;
+import java.lang.String;
+import javax.media.j3d.*;
+import com.sun.j3d.internal.J3dUtilsI18N;
+
+public class DistanceAttenuation
+{
+ // private fields
+
+ // public fields
+ /**
+ * Equation types
+ */
+ static final int DOUBLE_DISTANCE_HALF_GAIN = 1;
+
+ // methods
+ /**
+ * Fill a Distance Attenuation array
+ *
+ * recommend that the distance attenuation Point2f array is defined to
+ * be allocated to be 10 for DOUBLE_DISTANCE_HALF_GAIN - since 1/(2^10)
+ * exceeds 1/1000 scale that is agreed to be affective zero gain
+ *
+ * First method assumes that:
+ * type is half gain for every double of distance
+ * inner radius is 0.0 but region between 0th and 1st elements is constant
+ * since gains for these two elements are the same
+ * min gain approches zero.
+ */
+ public void fillDistanceAttenuation(
+ float unitDistance, float unitGain,
+ Point2f[] distanceAttenuation ) {
+ if (distanceAttenuation == null)
+ throw new SoundException(J3dUtilsI18N.getString("DistanceAttenuation0"));
+
+ int length = distanceAttenuation.length;
+ distanceAttenuation[0].x = 0.0f;
+ distanceAttenuation[0].y = unitGain;
+ float nextDistance = unitDistance;
+ float nextGain = unitGain;
+
+ for (int i=1; i<length; i++) {
+ distanceAttenuation[i].x = nextDistance;
+ distanceAttenuation[i].y = nextGain;
+ nextDistance *= 2.0f;
+ nextGain *= 0.5f;
+ }
+ }
+
+ public void fillDistanceAttenuation(
+ float innerRadius, float maxConstantGain,
+ float unitDistance, float unitGain,
+ int curveType, Point2f[] distanceAttenuation ) {
+ if (distanceAttenuation == null)
+ throw new SoundException(J3dUtilsI18N.getString("DistanceAttenuation0"));
+
+ int length = distanceAttenuation.length;
+ distanceAttenuation[0].x = innerRadius;
+ distanceAttenuation[0].y = maxConstantGain;
+ // Danger if mzxConstanceGain is less than greater than unitGain
+ // then your modeling attenuation that's physically improbable!
+ float nextDistance = unitDistance;
+ float nextGain = unitGain;
+
+ for (int i=1; i<length; i++) {
+ distanceAttenuation[i].x = innerRadius + nextDistance;
+ distanceAttenuation[i].y = nextGain;
+ nextDistance *= 2.0f;
+ nextGain *= 0.5f;
+ }
+ }
+
+ public void fillDistanceAttenuation(
+ float innerRadius, float maxConstantGain,
+ float unitDistance, float unitGain,
+ float outerRadius, float minConstantGain,
+ int curveType, Point2f[] distanceAttenuation ) {
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/CubicSplineCurve.java b/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/CubicSplineCurve.java
new file mode 100644
index 0000000..cdad5db
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/CubicSplineCurve.java
@@ -0,0 +1,175 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.interpolators;
+
+import javax.media.j3d.*;
+import java.util.*;
+import javax.vecmath.*;
+import com.sun.j3d.internal.J3dUtilsI18N;
+
+/**
+ * CubicSplineCurve is a container class that holds a number of
+ * cubicSplineSegments
+ *
+ * @since Java3D 1.1
+ */
+
+public class CubicSplineCurve {
+
+ private float totalCurveLength;
+ private CubicSplineSegment[] cubicSplineSegment;
+ public int numSegments;
+
+
+ /**
+ * Default constructor
+ */
+ CubicSplineCurve () {
+ numSegments = 0;
+ totalCurveLength = 0f;
+ }
+
+ /**
+ * This method takes a list of key frames and creates spline segments
+ * from it. It requires at least four key frames to be passed to it.
+ * Given n key frames, it creates n-3 CubicSplineSegments.
+ * @param keys the list of key frames that specify the motion path
+ */
+
+ CubicSplineCurve (TCBKeyFrame keys[]) {
+
+ int keyLength = keys.length;
+ // Require at least 4 key frames for cubic spline curve
+ if (keyLength < 4)
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("CubicSplineCurve0"));
+
+ numSegments = keyLength - 3;
+ this.cubicSplineSegment = new CubicSplineSegment[numSegments];
+
+ // intialize and calculate coefficients for each segment
+ int k0 = 0; int k1 = 1; int k2 = 2; int k3 = 3;
+ for (; k0 < numSegments; k0++, k1++, k2++, k3++) {
+ this.cubicSplineSegment[k0] = new CubicSplineSegment
+ (keys[k0], keys[k1], keys[k2], keys[k3]);
+ }
+
+ // compute total curve length
+ computeTotalCurveLength ();
+ }
+
+ /**
+ * This method takes a list of spline segments creates the
+ * CubicSplineCurve.
+ * @param the list of segments that comprise the complete motion path
+ */
+
+ CubicSplineCurve (CubicSplineSegment s[]) {
+
+ cubicSplineSegment = new CubicSplineSegment[s.length];
+ numSegments = cubicSplineSegment.length;
+ for (int i = 0; i < numSegments; i++) {
+ this.cubicSplineSegment[i] = s[i];
+ }
+
+ // compute total curve length
+ computeTotalCurveLength ();
+ }
+
+ /**
+ * This method takes a list of spline segments to replace the existing
+ * set of CubicSplineSegments that comprise the current CubicSplineCurve
+ * motion path.
+ * @param s the list of segments that comprise the complete motion path
+ */
+
+ public void setSegments (CubicSplineSegment s[]) {
+
+ cubicSplineSegment = new CubicSplineSegment[s.length];
+ numSegments = cubicSplineSegment.length;
+ for (int i = 0; i < numSegments; i++) {
+ this.cubicSplineSegment[i] = s[i];
+ }
+
+ // compute total curve length
+ computeTotalCurveLength ();
+ }
+
+ /**
+ * This method returns the CubicSplineSegments pointed to by index
+ * @param index the index of the CubicSplineSegment required
+ * @return index the CubicSplineSegment pointed to by index
+ */
+ public CubicSplineSegment getSegment (int index) {
+
+ return this.cubicSplineSegment[index];
+
+ }
+
+
+ // computes the total length of the curve
+ private void computeTotalCurveLength () {
+
+ totalCurveLength = 0f;
+ for (int i = 0; i < numSegments; i++) {
+ totalCurveLength += cubicSplineSegment[i].length;
+ }
+
+ }
+
+ /**
+ * This method returns the total length of the entire CubicSplineCurve
+ * motion path.
+ *
+ * @return the length of the CubicSplineCurve motion path
+ */
+
+ public float getTotalCurveLength () {
+
+ return this.totalCurveLength;
+
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/CubicSplineSegment.java b/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/CubicSplineSegment.java
new file mode 100644
index 0000000..5578b8e
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/CubicSplineSegment.java
@@ -0,0 +1,543 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.interpolators;
+
+import javax.media.j3d.*;
+import java.util.*;
+import javax.vecmath.*;
+
+
+/**
+ * The CubicSplineSegment class creates the representation of a
+ * TCB (Kochanek-Bartels Spline). This class takes 4 key frames as
+ * its input (using TCBKeyFrame). If interpolating between the i<sup>th</sup>
+ * and (i+1)<sup>th</sup> key frame then the four key frames that need to
+ * be specified are the (i-1)<sup>th</sup>, i<sup>th</sup>, (i+1)<sup>th</sup>
+ * and (i+2)<sup>th</sup> keyframes in order. The CubicSegmentClass
+ * then pre-computes the hermite interpolation basis coefficients if the
+ * (i+1)<sup>th</sup> frame has the linear flag set to zero. These are used to
+ * calculate the interpolated position, scale and quaternions when they
+ * requested by the user using the getInterpolated* methods. If the the
+ * (i+1)<sup>th</sup> frame's linear flag is set to 1 then the class uses
+ * linear interpolation to calculate the interpolated position, sccale and
+ * quaternions it returns through the getInterpolated* methods.
+ *
+ * @since Java3D 1.1
+ */
+
+public class CubicSplineSegment {
+
+ // Legendre polynomial information for Gaussian quadrature of speed
+ // for the domain [0,u], 0 <= u <= 1.
+
+ // Legendre roots mapped to (root+1)/2
+ static final double modRoot[] =
+ {
+ 0.046910077,
+ 0.230765345,
+ 0.5,
+ 0.769234655,
+ 0.953089922
+ };
+
+ // original coefficients divided by 2
+ static final double modCoeff[] =
+ {
+ 0.118463442,
+ 0.239314335,
+ 0.284444444,
+ 0.239314335,
+ 0.118463442
+ };
+
+ // Key Frames
+ TCBKeyFrame[] keyFrame = new TCBKeyFrame[4];
+
+ // H.C
+ Point3f c0, c1, c2, c3; // coefficients for position
+ Point3f e0, e1, e2, e3; // coefficients for scale
+
+ // variables for destination derivative
+ float one_minus_t_in;
+ float one_minus_c_in;
+ float one_minus_b_in;
+ float one_plus_c_in;
+ float one_plus_b_in;
+ float ddb;
+ float dda;
+
+ // variables for source derivative
+ float one_minus_t_out;
+ float one_minus_c_out;
+ float one_minus_b_out;
+ float one_plus_c_out;
+ float one_plus_b_out;
+ float dsb;
+ float dsa;
+
+ // Length of the spline segment
+ float length;
+
+ // interpolation type
+ int linear;
+
+ /**
+ * Default constructor
+ */
+ CubicSplineSegment () {
+
+ length = 0;
+
+ }
+
+ /**
+ * Creates a cubic spline segment between two key frames using the
+ * key frames provided. If creating a spline between the ith frame and
+ * the (i+1)<sup>th</sup> frame then send down the (i - 1)<sup>th</sup>,
+ * i<sup>th</sup> , (i+1)<sup>th</sup> and the (i+2)<sup>th</sup> key
+ * frames.
+ *
+ * @param kf0 (i - 1)<sup>th</sup> Key Frame
+ * @param kf1 i<sup>th</sup> Key Frame
+ * @param kf2 (i + 1)<sup>th</sup> Key Frame
+ * @param kf3 (i + 2)<sup>th</sup> Key Frame
+ */
+
+ CubicSplineSegment (TCBKeyFrame kf0, TCBKeyFrame kf1, TCBKeyFrame kf2,
+ TCBKeyFrame kf3) {
+
+ // Copy KeyFrame information
+ keyFrame[0] = new TCBKeyFrame(kf0);
+ keyFrame[1] = new TCBKeyFrame(kf1);
+ keyFrame[2] = new TCBKeyFrame(kf2);
+ keyFrame[3] = new TCBKeyFrame(kf3);
+
+ // if linear interpolation is requested then just set linear flag
+ // if spline interpolation is needed then compute spline coefficients
+ if (kf2.linear == 1) {
+ this.linear = 1;
+ } else {
+ this.linear = 0;
+ computeCommonCoefficients (kf0, kf1, kf2, kf3);
+ computeHermiteCoefficients (kf0, kf1, kf2, kf3);
+ }
+
+ length = computeLength (1.0f);
+ // System.out.println ("Segment length = " + length);
+
+ }
+
+ // compute the common coefficients
+ private void computeCommonCoefficients (TCBKeyFrame kf0,
+ TCBKeyFrame kf1,
+ TCBKeyFrame kf2,
+ TCBKeyFrame kf3) {
+
+ // variables for destination derivative
+ float one_minus_t_in = 1.0f - kf1.tension;
+ float one_minus_c_in = 1.0f - kf1.continuity;
+ float one_minus_b_in = 1.0f - kf1.bias;
+ float one_plus_c_in = 1.0f + kf1.continuity;
+ float one_plus_b_in = 1.0f + kf1.bias;
+
+ // coefficients for the incoming Tangent
+ ddb = one_minus_t_in * one_minus_c_in * one_minus_b_in;
+ dda = one_minus_t_in * one_plus_c_in * one_plus_b_in;
+
+ // variables for source derivative
+ float one_minus_t_out = 1.0f - kf2.tension;
+ float one_minus_c_out = 1.0f - kf2.continuity;
+ float one_minus_b_out = 1.0f - kf2.bias;
+ float one_plus_c_out = 1.0f + kf2.continuity;
+ float one_plus_b_out = 1.0f + kf2.bias;
+
+ // coefficients for the outgoing Tangent
+ dsb = one_minus_t_in * one_plus_c_in * one_minus_b_in;
+ dsa = one_minus_t_in * one_minus_c_in * one_plus_b_in;
+ }
+
+
+ // compute the hermite interpolation basis coefficients
+ private void computeHermiteCoefficients (TCBKeyFrame kf0,
+ TCBKeyFrame kf1,
+ TCBKeyFrame kf2,
+ TCBKeyFrame kf3) {
+
+
+ Point3f deltaP = new Point3f();
+ Point3f deltaS = new Point3f();
+
+ // Find the difference in position and scale
+ deltaP.x = kf2.position.x - kf1.position.x;
+ deltaP.y = kf2.position.y - kf1.position.y;
+ deltaP.z = kf2.position.z - kf1.position.z;
+
+ deltaS.x = kf2.scale.x - kf1.scale.x;
+ deltaS.y = kf2.scale.y - kf1.scale.y;
+ deltaS.z = kf2.scale.z - kf1.scale.z;
+
+ // Incoming Tangent
+ Point3f dd_pos = new Point3f();
+ Point3f dd_scale = new Point3f();
+
+ // If this is the first keyframe of the animation
+ if (kf0.knot == kf1.knot) {
+
+ float ddab = 0.5f * (dda + ddb);
+
+ // Position
+ dd_pos.x = ddab * deltaP.x;
+ dd_pos.y = ddab * deltaP.y;
+ dd_pos.z = ddab * deltaP.z;
+
+ // Scale
+ dd_scale.x = ddab * deltaS.x;
+ dd_scale.y = ddab * deltaS.y;
+ dd_scale.z = ddab * deltaS.z;
+
+ } else {
+
+ float adj0 = (kf1.knot - kf0.knot)/(kf2.knot - kf0.knot);
+
+ // Position
+ dd_pos.x = adj0 *
+ ((ddb * deltaP.x) + (dda * (kf1.position.x - kf0.position.x)));
+ dd_pos.y = adj0 *
+ ((ddb * deltaP.y) + (dda * (kf1.position.y - kf0.position.y)));
+ dd_pos.z = adj0 *
+ ((ddb * deltaP.z) + (dda * (kf1.position.z - kf0.position.z)));
+
+ // Scale
+ dd_scale.x = adj0 *
+ ((ddb * deltaS.x) + (dda * (kf1.scale.x - kf0.scale.x)));
+ dd_scale.y = adj0 *
+ ((ddb * deltaS.y) + (dda * (kf1.scale.y - kf0.scale.y)));
+ dd_scale.z = adj0 *
+ ((ddb * deltaS.z) + (dda * (kf1.scale.z - kf0.scale.z)));
+ }
+
+ // Outgoing Tangent
+ Point3f ds_pos = new Point3f();
+ Point3f ds_scale = new Point3f();
+
+ // If this is the last keyframe of the animation
+ if (kf2.knot == kf3.knot) {
+
+ float dsab = 0.5f * (dsa + dsb);
+
+ // Position
+ ds_pos.x = dsab * deltaP.x;
+ ds_pos.y = dsab * deltaP.y;
+ ds_pos.z = dsab * deltaP.z;
+
+ // Scale
+ ds_scale.x = dsab * deltaS.x;
+ ds_scale.y = dsab * deltaS.y;
+ ds_scale.z = dsab * deltaS.z;
+
+ } else {
+
+ float adj1 = (kf2.knot - kf1.knot)/(kf3.knot - kf1.knot);
+
+ // Position
+ ds_pos.x = adj1 *
+ ((dsb * (kf3.position.x - kf2.position.x)) + (dsa * deltaP.x));
+ ds_pos.y = adj1 *
+ ((dsb * (kf3.position.y - kf2.position.y)) + (dsa * deltaP.y));
+ ds_pos.z = adj1 *
+ ((dsb * (kf3.position.z - kf2.position.z)) + (dsa * deltaP.z));
+
+ // Scale
+ ds_scale.x = adj1 *
+ ((dsb * (kf3.scale.x - kf2.scale.x)) + (dsa * deltaS.x));
+ ds_scale.y = adj1 *
+ ((dsb * (kf3.scale.y - kf2.scale.y)) + (dsa * deltaS.y));
+ ds_scale.z = adj1 *
+ ((dsb * (kf3.scale.z - kf2.scale.z)) + (dsa * deltaS.z));
+ }
+
+ // Calculate the coefficients of the polynomial for position
+ c0 = new Point3f();
+ c0.x = kf1.position.x;
+ c0.y = kf1.position.y;
+ c0.z = kf1.position.z;
+
+ c1 = new Point3f();
+ c1.x = dd_pos.x;
+ c1.y = dd_pos.y;
+ c1.z = dd_pos.z;
+
+ c2 = new Point3f();
+ c2.x = 3*deltaP.x - 2*dd_pos.x - ds_pos.x;
+ c2.y = 3*deltaP.y - 2*dd_pos.y - ds_pos.y;
+ c2.z = 3*deltaP.z - 2*dd_pos.z - ds_pos.z;
+
+ c3 = new Point3f();
+ c3.x = -2*deltaP.x + dd_pos.x + ds_pos.x;
+ c3.y = -2*deltaP.y + dd_pos.y + ds_pos.y;
+ c3.z = -2*deltaP.z + dd_pos.z + ds_pos.z;
+
+ // Calculate the coefficients of the polynomial for scale
+ e0 = new Point3f();
+ e0.x = kf1.scale.x;
+ e0.y = kf1.scale.y;
+ e0.z = kf1.scale.z;
+
+ e1 = new Point3f();
+ e1.x = dd_scale.x;
+ e1.y = dd_scale.y;
+ e1.z = dd_scale.z;
+
+ e2 = new Point3f();
+ e2.x = 3*deltaS.x - 2*dd_scale.x - ds_scale.x;
+ e2.y = 3*deltaS.y - 2*dd_scale.y - ds_scale.y;
+ e2.z = 3*deltaS.z - 2*dd_scale.z - ds_scale.z;
+
+ e3 = new Point3f();
+ e3.x = -2*deltaS.x + dd_scale.x + ds_scale.x;
+ e3.y = -2*deltaS.y + dd_scale.y + ds_scale.y;
+ e3.z = -2*deltaS.z + dd_scale.z + ds_scale.z;
+ }
+
+
+ /**
+ * Computes the length of the curve at a given point between
+ * key frames.
+ * @param u specifies the point between keyframes where 0 <= u <= 1.
+ */
+
+ public float computeLength (float u) {
+
+ float result = 0f;
+
+ // if linear interpolation
+ if (linear == 1) {
+ result = u*keyFrame[2].position.distance(keyFrame[1].position);
+ } else {
+ // Need to transform domain [0,u] to [-1,1]. If 0 <= x <= u
+ // and -1 <= t <= 1, then x = u*(t+1)/2.
+ int degree = 5;
+ for (int i = 0; i < degree; i++)
+ result += (float)modCoeff[i]*computeSpeed(u*(float)modRoot[i]);
+ result *= u;
+ }
+
+ return result;
+ }
+
+ // Velocity along curve
+ private float computeSpeed (float u) {
+ Point3f v = new Point3f();
+
+ v.x = c1.x + u * (2 * c2.x + 3 * u * c3.x);
+ v.y = c1.y + u * (2 * c2.y + 3 * u * c3.y);
+ v.z = c1.z + u * (2 * c2.z + 3 * u * c3.z);
+
+ return (float)(Math.sqrt(v.x*v.x + v.y*v.y + v.z*v.z));
+ }
+
+
+ /**
+ * Computes the interpolated quaternion along the curve at
+ * a given point between key frames. This routine uses linear
+ * interpolation if the (i+1)<sup>th</sup> key frame's linear
+ * value is equal to 1.
+ *
+ * @param u specifies the point between keyframes where 0 <= u <= 1.
+ * @param newQuat returns the value of the interpolated quaternion
+ */
+
+ public void getInterpolatedQuaternion (float u, Quat4f newQuat) {
+
+ // if linear interpolation
+ if (this.linear == 1) {
+ double quatDot;
+
+ quatDot = keyFrame[1].quat.x * keyFrame[2].quat.x +
+ keyFrame[1].quat.y * keyFrame[2].quat.y +
+ keyFrame[1].quat.z * keyFrame[2].quat.z +
+ keyFrame[1].quat.w * keyFrame[2].quat.w;
+
+ if (quatDot < 0) {
+ newQuat.x = keyFrame[1].quat.x +
+ (-keyFrame[2].quat.x - keyFrame[1].quat.x) * u;
+ newQuat.y = keyFrame[1].quat.y +
+ (-keyFrame[2].quat.y - keyFrame[1].quat.y) * u;
+ newQuat.z = keyFrame[1].quat.z +
+ (-keyFrame[2].quat.z - keyFrame[1].quat.z) * u;
+ newQuat.w = keyFrame[1].quat.w +
+ (-keyFrame[2].quat.w - keyFrame[1].quat.w) * u;
+ } else {
+ newQuat.x = keyFrame[1].quat.x +
+ (keyFrame[2].quat.x - keyFrame[1].quat.x) * u;
+ newQuat.y = keyFrame[1].quat.y +
+ (keyFrame[2].quat.y - keyFrame[1].quat.y) * u;
+ newQuat.z = keyFrame[1].quat.z +
+ (keyFrame[2].quat.z - keyFrame[1].quat.z) * u;
+ newQuat.w = keyFrame[1].quat.w +
+ (keyFrame[2].quat.w - keyFrame[1].quat.w) * u;
+ }
+
+ } else {
+
+ // TODO:
+ // Currently we just use the great circle spherical interpolation
+ // for quaternions irrespective of the linear flag. Eventually
+ // we might want to do cubic interpolation of quaternions
+ newQuat.interpolate (keyFrame[1].quat, keyFrame[2].quat, u);
+ }
+
+ }
+
+
+
+ /**
+ * Computes the interpolated scale along the curve at a given point
+ * between key frames and returns a Point3f with the interpolated
+ * x, y, and z scale components. This routine uses linear
+ * interpolation if the (i+1)<sup>th</sup> key frame's linear
+ * value is equal to 1.
+ *
+ * @param u specifies the point between keyframes where 0 <= u <= 1.
+ * @param newScale returns the interpolated x,y,z scale value in a Point3f
+ */
+
+ public void getInterpolatedScale (float u, Point3f newScale) {
+
+ // if linear interpolation
+ if (this.linear == 1) {
+
+ newScale.x = keyFrame[1].scale.x +
+ ((keyFrame[2].scale.x - keyFrame[1].scale.x) * u);
+ newScale.y = keyFrame[1].scale.y +
+ ((keyFrame[2].scale.y - keyFrame[1].scale.y) * u);
+ newScale.z = keyFrame[1].scale.z +
+ ((keyFrame[2].scale.z - keyFrame[1].scale.z) * u);
+
+ } else {
+
+ newScale.x = e0.x + u * (e1.x + u * (e2.x + u * e3.x));
+ newScale.y = e0.y + u * (e1.y + u * (e2.y + u * e3.y));
+ newScale.z = e0.z + u * (e1.z + u * (e2.z + u * e3.z));
+
+ }
+ }
+
+
+ /**
+ * Computes the interpolated position along the curve at a given point
+ * between key frames and returns a Point3f with the interpolated
+ * x, y, and z scale components. This routine uses linear
+ * interpolation if the (i+1)<sup>th</sup> key frame's linear
+ * value is equal to 1.
+ *
+ * @param u specifies the point between keyframes where 0 <= u <= 1.
+ * @param newPos returns the interpolated x,y,z position in a Point3f
+ */
+
+ public void getInterpolatedPosition (float u, Point3f newPos) {
+
+ // if linear interpolation
+ if (this.linear == 1) {
+ newPos.x = keyFrame[1].position.x +
+ ((keyFrame[2].position.x - keyFrame[1].position.x) * u);
+ newPos.y = keyFrame[1].position.y +
+ ((keyFrame[2].position.y - keyFrame[1].position.y) * u);
+ newPos.z = keyFrame[1].position.z +
+ ((keyFrame[2].position.z - keyFrame[1].position.z) * u);
+ } else {
+
+ newPos.x = c0.x + u * (c1.x + u * (c2.x + u * c3.x));
+ newPos.y = c0.y + u * (c1.y + u * (c2.y + u * c3.y));
+ newPos.z = c0.z + u * (c1.z + u * (c2.z + u * c3.z));
+
+ }
+ }
+
+
+ /**
+ * Computes the interpolated position along the curve at a given point
+ * between key frames and returns a Vector3f with the interpolated
+ * x, y, and z scale components. This routine uses linear
+ * interpolation if the (i+1)<sup>th</sup> key frame's linear
+ * value is equal to 1.
+ *
+ * @param u specifies the point between keyframes where 0 <= u <= 1.
+ * @param newPos returns the interpolated x,y,z position in a Vector3f.
+ */
+
+ public void getInterpolatedPositionVector (float u, Vector3f newPos) {
+ // if linear interpolation
+ if (this.linear == 1) {
+ newPos.x = keyFrame[1].position.x +
+ ((keyFrame[2].position.x - keyFrame[1].position.x) * u);
+ newPos.y = keyFrame[1].position.y +
+ ((keyFrame[2].position.y - keyFrame[1].position.y) * u);
+ newPos.z = keyFrame[1].position.z +
+ ((keyFrame[2].position.z - keyFrame[1].position.z) * u);
+ } else {
+
+ newPos.x = c0.x + u * (c1.x + u * (c2.x + u * c3.x));
+ newPos.y = c0.y + u * (c1.y + u * (c2.y + u * c3.y));
+ newPos.z = c0.z + u * (c1.z + u * (c2.z + u * c3.z));
+
+ }
+ }
+
+ /**
+ * Computes the ratio of the length of the spline from the i<sup>th</sup>
+ * key frame to the position specified by u to the length of the entire
+ * spline segment from the i<sup>th</sup> key frame to the (i+1)
+ * <sup>th</sup> key frame. When the (i+1)<sup>th</sup> key frame's linear
+ * value is equal to 1, this is meaninful otherwise it should return u.
+ *
+ * @param u specifies the point between keyframes where 0 <= u <= 1.
+ * @return the interpolated ratio
+ */
+
+ public float getInterpolatedValue (float u) {
+ return (computeLength(u)/this.length);
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/KBCubicSplineCurve.java b/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/KBCubicSplineCurve.java
new file mode 100644
index 0000000..84fdc1d
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/KBCubicSplineCurve.java
@@ -0,0 +1,174 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.interpolators;
+
+import javax.media.j3d.*;
+import java.util.*;
+import javax.vecmath.*;
+import com.sun.j3d.internal.J3dUtilsI18N;
+
+/**
+ * KBCubicSplineCurve is a container class that holds a number of
+ * KBCubicSplineSegments
+ *
+ * @since Java3D 1.2
+ */
+
+public class KBCubicSplineCurve {
+
+ private float totalCurveLength;
+ private KBCubicSplineSegment[] cubicSplineSegment;
+ public int numSegments;
+
+
+ // default constructor
+ KBCubicSplineCurve () {
+ numSegments = 0;
+ totalCurveLength = 0f;
+ }
+
+ /**
+ * This method takes a list of key frames and creates spline segments
+ * from it. It requires at least four key frames to be passed to it.
+ * Given n key frames, it creates n-3 KBCubicSplineSegments.
+ * @param the list of key frames that specify the motion path
+ */
+
+ KBCubicSplineCurve (KBKeyFrame keys[]) {
+
+ int keyLength = keys.length;
+ // Require at least 4 key frames for cubic spline curve
+ if (keyLength < 4)
+ throw new
+ IllegalArgumentException(J3dUtilsI18N.getString("KBCubicSplineCurve0"));
+
+ numSegments = keyLength - 3;
+ this.cubicSplineSegment = new KBCubicSplineSegment[numSegments];
+
+ // intialize and calculate coefficients for each segment
+ int k0 = 0; int k1 = 1; int k2 = 2; int k3 = 3;
+ for (; k0 < numSegments; k0++, k1++, k2++, k3++) {
+ this.cubicSplineSegment[k0] = new KBCubicSplineSegment
+ (keys[k0], keys[k1], keys[k2], keys[k3]);
+ }
+
+ // compute total curve length
+ computeTotalCurveLength ();
+ }
+
+ /**
+ * This method takes a list of spline segments creates the
+ * KBCubicSplineCurve.
+ * @param the list of segments that comprise the complete motion path
+ */
+
+ KBCubicSplineCurve (KBCubicSplineSegment s[]) {
+
+ cubicSplineSegment = new KBCubicSplineSegment[s.length];
+ numSegments = cubicSplineSegment.length;
+ for (int i = 0; i < numSegments; i++) {
+ this.cubicSplineSegment[i] = s[i];
+ }
+
+ // compute total curve length
+ computeTotalCurveLength ();
+ }
+
+ /**
+ * This method takes a list of spline segments to replace the existing
+ * set of KBCubicSplineSegments that comprise the current
+ * KBCubicSplineCurve motion path.
+ * @param s the list of segments that comprise the complete motion path
+ */
+
+ public void setSegments (KBCubicSplineSegment s[]) {
+
+ cubicSplineSegment = new KBCubicSplineSegment[s.length];
+ numSegments = cubicSplineSegment.length;
+ for (int i = 0; i < numSegments; i++) {
+ this.cubicSplineSegment[i] = s[i];
+ }
+
+ // compute total curve length
+ computeTotalCurveLength ();
+ }
+
+ /**
+ * This method returns the KBCubicSplineSegments pointed to by index
+ * @param index the index of the KBCubicSplineSegment required
+ * @return the KBCubicSplineSegment pointed to by index
+ */
+ public KBCubicSplineSegment getSegment (int index) {
+
+ return this.cubicSplineSegment[index];
+
+ }
+
+
+ // computes the total length of the curve
+ private void computeTotalCurveLength () {
+
+ totalCurveLength = 0f;
+ for (int i = 0; i < numSegments; i++) {
+ totalCurveLength += cubicSplineSegment[i].length;
+ }
+
+ }
+
+ /**
+ * This method returns the total length of the entire KBCubicSplineCurve
+ * motion path.
+ *
+ * @return the length of the KBCubicSplineCurve motion path
+ */
+
+ public float getTotalCurveLength () {
+
+ return this.totalCurveLength;
+
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/KBCubicSplineSegment.java b/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/KBCubicSplineSegment.java
new file mode 100644
index 0000000..396b01e
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/KBCubicSplineSegment.java
@@ -0,0 +1,630 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.interpolators;
+
+import javax.media.j3d.*;
+import java.util.*;
+import javax.vecmath.*;
+
+
+/**
+ * The KBCubicSplineSegment class creates the representation of a
+ * Kochanek-Bartel's (also known as the TCB or Tension-Continuity-Bias
+ * Spline. This class takes 4 key frames as its input (using KBKeyFrame).
+ * If interpolating between the i<sup>th</sup> and (i+1)<sup>th</sup> key
+ * frame then the four key frames that need to be specified are the
+ * (i-1)<sup>th</sup>, i<sup>th</sup>, (i+1)<sup>th</sup>
+ * and (i+2)<sup>th</sup> keyframes in order. The KBCubicSegmentClass
+ * then pre-computes the hermite interpolation basis coefficients if the
+ * (i+1)<sup>th</sup> frame has the linear flag set to zero. These are used to
+ * calculate the interpolated position, scale and quaternions when they
+ * requested by the user using the getInterpolated* methods. If the the
+ * (i+1)<sup>th</sup> frame's linear flag is set to 1 then the class uses
+ * linear interpolation to calculate the interpolated position, scale, heading
+ * pitch and bank it returns through the getInterpolated* methods.
+ *
+ * @since Java3D 1.2
+ */
+public class KBCubicSplineSegment {
+
+ // Legendre polynomial information for Gaussian quadrature of speed
+ // for the domain [0,u], 0 <= u <= 1.
+
+ // Legendre roots mapped to (root+1)/2
+ static final double modRoot[] =
+ {
+ 0.046910077,
+ 0.230765345,
+ 0.5,
+ 0.769234655,
+ 0.953089922
+ };
+
+ // original coefficients divided by 2
+ static final double modCoeff[] =
+ {
+ 0.118463442,
+ 0.239314335,
+ 0.284444444,
+ 0.239314335,
+ 0.118463442
+ };
+
+ // Key Frames
+ KBKeyFrame[] keyFrame = new KBKeyFrame[4];
+
+ // H.C
+ Point3f c0, c1, c2, c3; // coefficients for position
+ Point3f e0, e1, e2, e3; // coefficients for scale
+ float h0, h1, h2, h3; // coefficients for heading
+ float p0, p1, p2, p3; // coefficients for pitch
+ float b0, b1, b2, b3; // coefficients for bank
+
+ // variables for destination derivative
+ float one_minus_t_in;
+ float one_minus_c_in;
+ float one_minus_b_in;
+ float one_plus_c_in;
+ float one_plus_b_in;
+ float ddb;
+ float dda;
+
+ // variables for source derivative
+ float one_minus_t_out;
+ float one_minus_c_out;
+ float one_minus_b_out;
+ float one_plus_c_out;
+ float one_plus_b_out;
+ float dsb;
+ float dsa;
+
+ // Length of the spline segment
+ float length;
+
+ // interpolation type
+ int linear;
+
+ // Default constructor
+ KBCubicSplineSegment () {
+
+ length = 0;
+
+ }
+
+ /**
+ * Creates a cubic spline segment between two key frames using the
+ * key frames provided. If creating a spline between the ith frame and
+ * the (i+1)<sup>th</sup> frame then send down the (i - 1)<sup>th</sup>,
+ * i<sup>th</sup> , (i+1)<sup>th</sup> and the (i+2)<sup>th</sup> key
+ * frames.
+ *
+ * @param kf0 (i - 1)<sup>th</sup> Key Frame
+ * @param kf1 i<sup>th</sup> Key Frame
+ * @param kf2 (i + 1)<sup>th</sup> Key Frame
+ * @param kf3 (i + 2)<sup>th</sup> Key Frame
+ */
+
+ KBCubicSplineSegment (KBKeyFrame kf0, KBKeyFrame kf1, KBKeyFrame kf2,
+ KBKeyFrame kf3) {
+
+ // Copy KeyFrame information
+ keyFrame[0] = new KBKeyFrame(kf0);
+ keyFrame[1] = new KBKeyFrame(kf1);
+ keyFrame[2] = new KBKeyFrame(kf2);
+ keyFrame[3] = new KBKeyFrame(kf3);
+
+ // if linear interpolation is requested then just set linear flag
+ // if spline interpolation is needed then compute spline coefficients
+ if (kf2.linear == 1) {
+ this.linear = 1;
+ } else {
+ this.linear = 0;
+ computeCommonCoefficients (kf0, kf1, kf2, kf3);
+ computeHermiteCoefficients (kf0, kf1, kf2, kf3);
+ }
+
+ length = computeLength (1.0f);
+ // System.out.println ("Segment length = " + length);
+
+ }
+
+ // compute the common coefficients
+ private void computeCommonCoefficients (KBKeyFrame kf0,
+ KBKeyFrame kf1,
+ KBKeyFrame kf2,
+ KBKeyFrame kf3) {
+
+ // variables for destination derivative
+ float one_minus_t_in = 1.0f - kf1.tension;
+ float one_minus_c_in = 1.0f - kf1.continuity;
+ float one_minus_b_in = 1.0f - kf1.bias;
+ float one_plus_c_in = 1.0f + kf1.continuity;
+ float one_plus_b_in = 1.0f + kf1.bias;
+
+ // coefficients for the incoming Tangent
+ ddb = one_minus_t_in * one_minus_c_in * one_minus_b_in;
+ dda = one_minus_t_in * one_plus_c_in * one_plus_b_in;
+
+ // variables for source derivative
+ float one_minus_t_out = 1.0f - kf2.tension;
+ float one_minus_c_out = 1.0f - kf2.continuity;
+ float one_minus_b_out = 1.0f - kf2.bias;
+ float one_plus_c_out = 1.0f + kf2.continuity;
+ float one_plus_b_out = 1.0f + kf2.bias;
+
+ // coefficients for the outgoing Tangent
+ dsb = one_minus_t_in * one_plus_c_in * one_minus_b_in;
+ dsa = one_minus_t_in * one_minus_c_in * one_plus_b_in;
+ }
+
+
+ // compute the hermite interpolation basis coefficients
+ private void computeHermiteCoefficients (KBKeyFrame kf0,
+ KBKeyFrame kf1,
+ KBKeyFrame kf2,
+ KBKeyFrame kf3) {
+
+
+ Point3f deltaP = new Point3f();
+ Point3f deltaS = new Point3f();
+ float deltaH;
+ float deltaT;
+ float deltaB;
+
+ // Find the difference in position and scale
+ deltaP.x = kf2.position.x - kf1.position.x;
+ deltaP.y = kf2.position.y - kf1.position.y;
+ deltaP.z = kf2.position.z - kf1.position.z;
+
+ deltaS.x = kf2.scale.x - kf1.scale.x;
+ deltaS.y = kf2.scale.y - kf1.scale.y;
+ deltaS.z = kf2.scale.z - kf1.scale.z;
+
+ // Find the difference in heading, pitch, and bank
+ deltaH = kf2.heading - kf1.heading;
+ deltaT = kf2.pitch - kf1.pitch;
+ deltaB = kf2.bank - kf1.bank;
+
+ // Incoming Tangent
+ Point3f dd_pos = new Point3f();
+ Point3f dd_scale = new Point3f();
+ float dd_heading, dd_pitch, dd_bank;
+
+ // If this is the first keyframe of the animation
+ if (kf0.knot == kf1.knot) {
+
+ float ddab = 0.5f * (dda + ddb);
+
+ // Position
+ dd_pos.x = ddab * deltaP.x;
+ dd_pos.y = ddab * deltaP.y;
+ dd_pos.z = ddab * deltaP.z;
+
+ // Scale
+ dd_scale.x = ddab * deltaS.x;
+ dd_scale.y = ddab * deltaS.y;
+ dd_scale.z = ddab * deltaS.z;
+
+ // Heading, Pitch and Bank
+ dd_heading = ddab * deltaH;
+ dd_pitch = ddab * deltaT;
+ dd_bank = ddab * deltaB;
+
+ } else {
+
+ float adj0 = (kf1.knot - kf0.knot)/(kf2.knot - kf0.knot);
+
+ // Position
+ dd_pos.x = adj0 *
+ ((ddb * deltaP.x) + (dda * (kf1.position.x - kf0.position.x)));
+ dd_pos.y = adj0 *
+ ((ddb * deltaP.y) + (dda * (kf1.position.y - kf0.position.y)));
+ dd_pos.z = adj0 *
+ ((ddb * deltaP.z) + (dda * (kf1.position.z - kf0.position.z)));
+
+ // Scale
+ dd_scale.x = adj0 *
+ ((ddb * deltaS.x) + (dda * (kf1.scale.x - kf0.scale.x)));
+ dd_scale.y = adj0 *
+ ((ddb * deltaS.y) + (dda * (kf1.scale.y - kf0.scale.y)));
+ dd_scale.z = adj0 *
+ ((ddb * deltaS.z) + (dda * (kf1.scale.z - kf0.scale.z)));
+
+ // Heading, Pitch and Bank
+ dd_heading = adj0 *
+ ((ddb * deltaH) + (dda * (kf1.heading - kf0.heading)));
+ dd_pitch = adj0 *
+ ((ddb * deltaT) + (dda * (kf1.pitch - kf0.pitch)));
+ dd_bank = adj0 *
+ ((ddb * deltaB) + (dda * (kf1.bank - kf0.bank)));
+ }
+
+ // Outgoing Tangent
+ Point3f ds_pos = new Point3f();
+ Point3f ds_scale = new Point3f();
+ float ds_heading, ds_pitch, ds_bank;
+
+ // If this is the last keyframe of the animation
+ if (kf2.knot == kf3.knot) {
+
+ float dsab = 0.5f * (dsa + dsb);
+
+ // Position
+ ds_pos.x = dsab * deltaP.x;
+ ds_pos.y = dsab * deltaP.y;
+ ds_pos.z = dsab * deltaP.z;
+
+ // Scale
+ ds_scale.x = dsab * deltaS.x;
+ ds_scale.y = dsab * deltaS.y;
+ ds_scale.z = dsab * deltaS.z;
+
+ // Heading, Pitch and Bank
+ ds_heading = dsab * deltaH;
+ ds_pitch = dsab * deltaT;
+ ds_bank = dsab * deltaB;
+
+ } else {
+
+ float adj1 = (kf2.knot - kf1.knot)/(kf3.knot - kf1.knot);
+
+ // Position
+ ds_pos.x = adj1 *
+ ((dsb * (kf3.position.x - kf2.position.x)) + (dsa * deltaP.x));
+ ds_pos.y = adj1 *
+ ((dsb * (kf3.position.y - kf2.position.y)) + (dsa * deltaP.y));
+ ds_pos.z = adj1 *
+ ((dsb * (kf3.position.z - kf2.position.z)) + (dsa * deltaP.z));
+
+ // Scale
+ ds_scale.x = adj1 *
+ ((dsb * (kf3.scale.x - kf2.scale.x)) + (dsa * deltaS.x));
+ ds_scale.y = adj1 *
+ ((dsb * (kf3.scale.y - kf2.scale.y)) + (dsa * deltaS.y));
+ ds_scale.z = adj1 *
+ ((dsb * (kf3.scale.z - kf2.scale.z)) + (dsa * deltaS.z));
+
+ // Heading, Pitch and Bank
+ ds_heading = adj1 *
+ ((dsb * (kf3.heading - kf2.heading)) + (dsa * deltaH));
+ ds_pitch = adj1 *
+ ((dsb * (kf3.pitch - kf2.pitch)) + (dsa * deltaT));
+ ds_bank = adj1 *
+ ((dsb * (kf3.bank - kf2.bank)) + (dsa * deltaB));
+ }
+
+ // Calculate the coefficients of the polynomial for position
+ c0 = new Point3f();
+ c0.x = kf1.position.x;
+ c0.y = kf1.position.y;
+ c0.z = kf1.position.z;
+
+ c1 = new Point3f();
+ c1.x = dd_pos.x;
+ c1.y = dd_pos.y;
+ c1.z = dd_pos.z;
+
+ c2 = new Point3f();
+ c2.x = 3*deltaP.x - 2*dd_pos.x - ds_pos.x;
+ c2.y = 3*deltaP.y - 2*dd_pos.y - ds_pos.y;
+ c2.z = 3*deltaP.z - 2*dd_pos.z - ds_pos.z;
+
+ c3 = new Point3f();
+ c3.x = -2*deltaP.x + dd_pos.x + ds_pos.x;
+ c3.y = -2*deltaP.y + dd_pos.y + ds_pos.y;
+ c3.z = -2*deltaP.z + dd_pos.z + ds_pos.z;
+
+ // Calculate the coefficients of the polynomial for scale
+ e0 = new Point3f();
+ e0.x = kf1.scale.x;
+ e0.y = kf1.scale.y;
+ e0.z = kf1.scale.z;
+
+ e1 = new Point3f();
+ e1.x = dd_scale.x;
+ e1.y = dd_scale.y;
+ e1.z = dd_scale.z;
+
+ e2 = new Point3f();
+ e2.x = 3*deltaS.x - 2*dd_scale.x - ds_scale.x;
+ e2.y = 3*deltaS.y - 2*dd_scale.y - ds_scale.y;
+ e2.z = 3*deltaS.z - 2*dd_scale.z - ds_scale.z;
+
+ e3 = new Point3f();
+ e3.x = -2*deltaS.x + dd_scale.x + ds_scale.x;
+ e3.y = -2*deltaS.y + dd_scale.y + ds_scale.y;
+ e3.z = -2*deltaS.z + dd_scale.z + ds_scale.z;
+
+ // Calculate the coefficients of the polynomial for heading, pitch
+ // and bank
+ h0 = kf1.heading;
+ p0 = kf1.pitch;
+ b0 = kf1.bank;
+
+ h1 = dd_heading;
+ p1 = dd_pitch;
+ b1 = dd_bank;
+
+ h2 = 3*deltaH - 2*dd_heading - ds_heading;
+ p2 = 3*deltaT - 2*dd_pitch - ds_pitch;
+ b2 = 3*deltaB - 2*dd_bank - ds_bank;
+
+ h3 = -2*deltaH + dd_heading + ds_heading;
+ p3 = -2*deltaT + dd_pitch + ds_pitch;
+ b3 = -2*deltaB + dd_bank + ds_bank;
+ }
+
+
+ /**
+ * Computes the length of the curve at a given point between
+ * key frames.
+ * @param u specifies the point between keyframes where 0 <= u <= 1.
+ */
+
+ public float computeLength (float u) {
+
+ float result = 0f;
+
+ // if linear interpolation
+ if (linear == 1) {
+ result = u*keyFrame[2].position.distance(keyFrame[1].position);
+ } else {
+ // Need to transform domain [0,u] to [-1,1]. If 0 <= x <= u
+ // and -1 <= t <= 1, then x = u*(t+1)/2.
+ int degree = 5;
+ for (int i = 0; i < degree; i++)
+ result += (float)modCoeff[i]*computeSpeed(u*(float)modRoot[i]);
+ result *= u;
+ }
+
+ return result;
+ }
+
+ // Velocity along curve
+ private float computeSpeed (float u) {
+ Point3f v = new Point3f();
+
+ v.x = c1.x + u * (2 * c2.x + 3 * u * c3.x);
+ v.y = c1.y + u * (2 * c2.y + 3 * u * c3.y);
+ v.z = c1.z + u * (2 * c2.z + 3 * u * c3.z);
+
+ return (float)(Math.sqrt(v.x*v.x + v.y*v.y + v.z*v.z));
+ }
+
+
+ /**
+ * Computes the interpolated scale along the curve at a given point
+ * between key frames and returns a Point3f with the interpolated
+ * x, y, and z scale components. This routine uses linear
+ * interpolation if the (i+1)<sup>th</sup> key frame's linear
+ * value is equal to 1.
+ *
+ * @param u specifies the point between keyframes where 0 <= u <= 1.
+ * @param newScale returns the interpolated x,y,z scale value in a Point3f
+ */
+
+ public void getInterpolatedScale (float u, Point3f newScale) {
+
+ // if linear interpolation
+ if (this.linear == 1) {
+
+ newScale.x = keyFrame[1].scale.x +
+ ((keyFrame[2].scale.x - keyFrame[1].scale.x) * u);
+ newScale.y = keyFrame[1].scale.y +
+ ((keyFrame[2].scale.y - keyFrame[1].scale.y) * u);
+ newScale.z = keyFrame[1].scale.z +
+ ((keyFrame[2].scale.z - keyFrame[1].scale.z) * u);
+
+ } else {
+
+ newScale.x = e0.x + u * (e1.x + u * (e2.x + u * e3.x));
+ newScale.y = e0.y + u * (e1.y + u * (e2.y + u * e3.y));
+ newScale.z = e0.z + u * (e1.z + u * (e2.z + u * e3.z));
+
+ }
+ }
+
+
+ /**
+ * Computes the interpolated position along the curve at a given point
+ * between key frames and returns a Point3f with the interpolated
+ * x, y, and z scale components. This routine uses linear
+ * interpolation if the (i+1)<sup>th</sup> key frame's linear
+ * value is equal to 1.
+ *
+ * @param u specifies the point between keyframes where 0 <= u <= 1.
+ * @param newPos returns the interpolated x,y,z position in a Point3f
+ */
+
+ public void getInterpolatedPosition (float u, Point3f newPos) {
+
+ // if linear interpolation
+ if (this.linear == 1) {
+ newPos.x = keyFrame[1].position.x +
+ ((keyFrame[2].position.x - keyFrame[1].position.x) * u);
+ newPos.y = keyFrame[1].position.y +
+ ((keyFrame[2].position.y - keyFrame[1].position.y) * u);
+ newPos.z = keyFrame[1].position.z +
+ ((keyFrame[2].position.z - keyFrame[1].position.z) * u);
+ } else {
+
+ newPos.x = c0.x + u * (c1.x + u * (c2.x + u * c3.x));
+ newPos.y = c0.y + u * (c1.y + u * (c2.y + u * c3.y));
+ newPos.z = c0.z + u * (c1.z + u * (c2.z + u * c3.z));
+
+ }
+ }
+
+
+ /**
+ * Computes the interpolated position along the curve at a given point
+ * between key frames and returns a Vector3f with the interpolated
+ * x, y, and z scale components. This routine uses linear
+ * interpolation if the (i+1)<sup>th</sup> key frame's linear
+ * value is equal to 1.
+ *
+ * @param u specifies the point between keyframes where 0 <= u <= 1.
+ * @param newPos returns the interpolated x,y,z position in a Vector3f.
+ */
+
+ public void getInterpolatedPositionVector (float u, Vector3f newPos) {
+ // if linear interpolation
+ if (this.linear == 1) {
+ newPos.x = keyFrame[1].position.x +
+ ((keyFrame[2].position.x - keyFrame[1].position.x) * u);
+ newPos.y = keyFrame[1].position.y +
+ ((keyFrame[2].position.y - keyFrame[1].position.y) * u);
+ newPos.z = keyFrame[1].position.z +
+ ((keyFrame[2].position.z - keyFrame[1].position.z) * u);
+ } else {
+
+ newPos.x = c0.x + u * (c1.x + u * (c2.x + u * c3.x));
+ newPos.y = c0.y + u * (c1.y + u * (c2.y + u * c3.y));
+ newPos.z = c0.z + u * (c1.z + u * (c2.z + u * c3.z));
+
+ }
+ }
+
+ /**
+ * Computes the interpolated heading along the curve at a given point
+ * between key frames and returns the interpolated value as a float
+ * This routine uses linear interpolation if the (i+1)<sup>th</sup>
+ * key frame's linear value is equal to 1.
+ *
+ * @param u specifies the point between keyframes where 0 <= u <= 1.
+ * @return returns the interpolated heading value
+ */
+
+ public float getInterpolatedHeading (float u) {
+
+ float newHeading;
+
+ // if linear interpolation
+ if (this.linear == 1) {
+
+ newHeading = keyFrame[1].heading +
+ ((keyFrame[2].heading - keyFrame[1].heading) * u);
+ } else {
+
+ newHeading = h0 + u * (h1 + u * (h2 + u * h3));
+
+ }
+
+ return newHeading;
+ }
+
+
+ /**
+ * Computes the interpolated pitch along the curve at a given point
+ * between key frames and returns the interpolated value as a float
+ * This routine uses linear interpolation if the (i+1)<sup>th</sup>
+ * key frame's linear value is equal to 1.
+ *
+ * @param u specifies the point between keyframes where 0 <= u <= 1.
+ * @return returns the interpolated pitch value
+ */
+
+ public float getInterpolatedPitch (float u) {
+
+ float newPitch;
+
+ // if linear interpolation
+ if (this.linear == 1) {
+
+ newPitch = keyFrame[1].pitch +
+ ((keyFrame[2].pitch - keyFrame[1].pitch) * u);
+ } else {
+
+ newPitch = p0 + u * (p1 + u * (p2 + u * p3));
+
+ }
+
+ return newPitch;
+ }
+
+ /**
+ * Computes the interpolated bank along the curve at a given point
+ * between key frames and returns the interpolated value as a float
+ * This routine uses linear interpolation if the (i+1)<sup>th</sup>
+ * key frame's linear value is equal to 1.
+ *
+ * @param u specifies the point between keyframes where 0 <= u <= 1.
+ * @return returns the interpolated bank value
+ */
+
+ public float getInterpolatedBank (float u) {
+
+ float newBank;
+
+ // if linear interpolation
+ if (this.linear == 1) {
+
+ newBank = keyFrame[1].bank +
+ ((keyFrame[2].bank - keyFrame[1].bank) * u);
+ } else {
+
+ newBank = b0 + u * (b1 + u * (b2 + u * b3));
+
+ }
+
+ return newBank;
+ }
+
+
+ /**
+ * Computes the ratio of the length of the spline from the i<sup>th</sup>
+ * key frame to the position specified by u to the length of the entire
+ * spline segment from the i<sup>th</sup> key frame to the (i+1)
+ * <sup>th</sup> key frame. When the (i+1)<sup>th</sup> key frame's linear
+ * value is equal to 1, this is meaninful otherwise it should return u.
+ *
+ * @param u specifies the point between keyframes where 0 <= u <= 1.
+ * @return the interpolated ratio
+ */
+
+ public float getInterpolatedValue (float u) {
+ return (computeLength(u)/this.length);
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/KBKeyFrame.java b/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/KBKeyFrame.java
new file mode 100644
index 0000000..7c2bb33
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/KBKeyFrame.java
@@ -0,0 +1,150 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.interpolators;
+
+import javax.media.j3d.*;
+import java.util.*;
+import javax.vecmath.*;
+import com.sun.j3d.internal.J3dUtilsI18N;
+
+/**
+ * This class represents a Key Frame that can be used for Kochanek-Bartels
+ * (also called TCB or Tension-Continuity-Bias Splines) spline interpolation.
+ *
+ * @since Java3D 1.2
+ */
+public class KBKeyFrame {
+
+ // Position, Rotation and Scale
+ public Point3f position;
+ public float heading;
+ public float pitch;
+ public float bank;
+ public Point3f scale;
+
+ // Tension, Continuity & Bias
+ public float tension;
+ public float continuity;
+ public float bias;
+
+ // Sample Time
+ public float knot;
+
+ // Interpolation type (linear = 0 -> spline interpolation)
+ public int linear;
+
+ // default constructor
+ KBKeyFrame () {
+ tension = continuity = bias = 0.0f;
+ }
+
+ public KBKeyFrame (KBKeyFrame kf) {
+ this(kf.knot, kf.linear, kf.position, kf.heading, kf.pitch, kf.bank,
+ kf.scale, kf.tension, kf.continuity, kf.bias);
+
+ }
+
+
+ /**
+ * Creates a key frame using the given inputs.
+ *
+ * @param k knot value for this key frame
+ * @param l the linear flag (0 - Spline Interp, 1, Linear Interp
+ * @param pos the position at the key frame
+ * @param hd the heading value at the key frame
+ * @param pi the pitch value at the key frame
+ * @param bk the bank value at the key frame
+ * @param s the scales at the key frame
+ * @param t tension (-1.0 < t < 1.0)
+ * @param c continuity (-1.0 < c < 1.0)
+ * @param b bias (-1.0 < b < 1.0)
+ */
+ public KBKeyFrame (float k, int l, Point3f pos, float hd, float pi,
+ float bk, Point3f s, float t, float c, float b) {
+
+ knot = k;
+ linear = l;
+ position = new Point3f(pos);
+ heading = hd;
+ pitch = pi;
+ bank = bk;
+ scale = new Point3f(s);
+
+ // Check for valid tension continuity and bias values
+ if (t < -1.0f || t > 1.0f) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("KBKeyFrame0"));
+ }
+
+ if (b < -1.0f || b > 1.0f) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("KBKeyFrame1"));
+ }
+
+ if (c < -1.0f || c > 1.0f) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("KBKeyFrame2"));
+ }
+
+ // copy valid tension, continuity and bias values
+ tension = t;
+ continuity = c;
+ bias = b;
+ }
+
+ /**
+ * Prints information comtained in this key frame
+ * @param tag string tag for identifying debug message
+ */
+ public void debugPrint (String tag) {
+ System.out.println ("\n" + tag);
+ System.out.println (" knot = " + knot);
+ System.out.println (" linear = " + linear);
+ System.out.println (" position(x,y,z) = " + position.x + " "
+ + position.y + " " + position.z);
+
+ System.out.println (" tension = " + tension);
+ System.out.println (" continuity = " + continuity);
+ System.out.println (" bias = " + bias);
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/KBRotPosScaleSplinePathInterpolator.java b/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/KBRotPosScaleSplinePathInterpolator.java
new file mode 100644
index 0000000..e684396
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/KBRotPosScaleSplinePathInterpolator.java
@@ -0,0 +1,313 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.interpolators;
+
+import javax.media.j3d.*;
+import java.util.*;
+import javax.vecmath.*;
+
+
+/**
+ * KBRotPosScaleSplinePathInterpolator; A rotation and position path
+ * interpolation behavior node using Kochanek-Bartels cubic splines
+ * (also known as TCB or Tension-Continuity-Bias Splines).
+ *
+ * @since Java3D 1.2
+ */
+
+/**
+ * KBRotPosScaleSplinePathInterpolator behavior. This class defines a
+ * behavior that varies the rotational, translational, and scale components
+ * of its target TransformGroup by using the Kochanek-Bartels cubic spline
+ * interpolation to interpolate among a series of key frames
+ * (using the value generated by the specified Alpha object). The
+ * interpolated position, orientation, and scale are used to generate
+ * a transform in the local coordinate system of this interpolator.
+ */
+
+public class KBRotPosScaleSplinePathInterpolator
+ extends KBSplinePathInterpolator {
+
+ private Transform3D rotation = new Transform3D();
+
+ private Matrix4d pitchMat = new Matrix4d(); // pitch matrix
+ private Matrix4d bankMat = new Matrix4d(); // bank matrix
+ private Matrix4d tMat = new Matrix4d(); // transformation matrix
+ private Matrix4d sMat = new Matrix4d(); // scale matrix
+ //Quat4f iQuat = new Quat4f(); // interpolated quaternion
+ private Vector3f iPos = new Vector3f(); // interpolated position
+ private Point3f iScale = new Point3f(); // interpolated scale
+ float iHeading, iPitch, iBank; // interpolated heading,
+ // pitch and bank
+
+ KBCubicSplineCurve cubicSplineCurve = new KBCubicSplineCurve();
+ KBCubicSplineSegment cubicSplineSegments[];
+ int numSegments;
+ int currentSegmentIndex;
+ KBCubicSplineSegment currentSegment;
+
+ // non-public, default constructor used by cloneNode
+ KBRotPosScaleSplinePathInterpolator() {
+ }
+
+ /**
+ * Constructs a new KBRotPosScaleSplinePathInterpolator object that
+ * varies the rotation, translation, and scale of the target
+ * TransformGroup's transform. At least 2 key frames are required for
+ * this interpolator. The first key
+ * frame's knot must have a value of 0.0 and the last knot must have a
+ * value of 1.0. An intermediate key frame with index k must have a
+ * knot value strictly greater than the knot value of a key frame with
+ * index less than k.
+ * @param alpha the alpha object for this interpolator
+ * @param target the TransformGroup node affected by this interpolator
+ * @param axisOfTransform the transform that specifies the local
+ * coordinate system in which this interpolator operates.
+ * @param keys an array of key frames that defien the motion path
+ */
+ public KBRotPosScaleSplinePathInterpolator(Alpha alpha,
+ TransformGroup target,
+ Transform3D axisOfTransform,
+ KBKeyFrame keys[]) {
+ super(alpha,target, axisOfTransform, keys);
+
+ // Create a spline curve using the derived key frames
+ cubicSplineCurve = new KBCubicSplineCurve(this.keyFrames);
+ numSegments = cubicSplineCurve.numSegments;
+
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>TransformInterpolator.setTransformAxis(Transform3D)</code>
+ */
+ public void setAxisOfRotPosScale(Transform3D axisOfRotPosScale) {
+ setTransformAxis(axisOfRotPosScale);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>TransformInterpolator.getTransformAxis()</code>
+ */
+ public Transform3D getAxisOfRotPosScale() {
+ return getTransformAxis();
+ }
+
+ /**
+ * Set the key frame at the specified index to <code>keyFrame</code>
+ * @param index Index of the key frame to change
+ * @param keyFrame The new key frame
+ */
+ public void setKeyFrame( int index, KBKeyFrame keyFrame ) {
+ super.setKeyFrame( index, keyFrame );
+
+ // TODO Optimize this
+ // Create a spline curve using the derived key frames
+ cubicSplineCurve = new KBCubicSplineCurve(this.keyFrames);
+ numSegments = cubicSplineCurve.numSegments;
+ }
+
+ /**
+ * Set all the key frames
+ * @param keyFrame The new key frames
+ */
+ public void setKeyFrames( KBKeyFrame[] keyFrame ) {
+ super.setKeyFrames( keyFrame );
+
+ // Create a spline curve using the derived key frames
+ cubicSplineCurve = new KBCubicSplineCurve(this.keyFrames);
+ numSegments = cubicSplineCurve.numSegments;
+ }
+
+ /**
+ * Computes the new transform for this interpolator for a given
+ * alpha value.
+ *
+ * @param alphaValue alpha value between 0.0 and 1.0
+ * @param transform object that receives the computed transform for
+ * the specified alpha value
+ *
+ * @since Java 3D 1.3
+ */
+ public void computeTransform(float alphaValue, Transform3D transform) {
+ // compute the current value of u from alpha and the
+ // determine lower and upper knot points
+ computePathInterpolation( alphaValue );
+
+ // Determine the segment within which we will be interpolating
+ currentSegmentIndex = this.lowerKnot - 1;
+
+ // if we are at the start of the curve
+ if (currentSegmentIndex == 0 && currentU == 0f) {
+
+ iHeading = keyFrames[1].heading;
+ iPitch = keyFrames[1].pitch;
+ iBank = keyFrames[1].bank;
+ iPos.set(keyFrames[1].position);
+ iScale.set(keyFrames[1].scale);
+
+ // if we are at the end of the curve
+ } else if (currentSegmentIndex == (numSegments-1) &&
+ currentU == 1.0) {
+
+ iHeading = keyFrames[upperKnot].heading;
+ iPitch = keyFrames[upperKnot].pitch;
+ iBank = keyFrames[upperKnot].bank;
+ iPos.set(keyFrames[upperKnot].position);
+ iScale.set(keyFrames[upperKnot].scale);
+
+ // if we are somewhere in between the curve
+ } else {
+
+ // Get a reference to the current spline segment i.e. the
+ // one bounded by lowerKnot and upperKnot
+ currentSegment
+ = cubicSplineCurve.getSegment(currentSegmentIndex);
+
+ // interpolate quaternions
+ iHeading = currentSegment.getInterpolatedHeading (currentU);
+ iPitch = currentSegment.getInterpolatedPitch (currentU);
+ iBank = currentSegment.getInterpolatedBank (currentU);
+
+ // interpolate position
+ currentSegment.getInterpolatedPositionVector(currentU,iPos);
+
+ // interpolate position
+ currentSegment.getInterpolatedScale(currentU,iScale);
+
+ }
+
+ // Generate a transformation matrix in tMat using interpolated
+ // heading, pitch and bank
+ pitchMat.setIdentity();
+ pitchMat.rotX(-iPitch);
+ bankMat.setIdentity();
+ bankMat.rotZ(iBank);
+ tMat.setIdentity();
+ tMat.rotY(-iHeading);
+ tMat.mul(pitchMat);
+ tMat.mul(bankMat);
+
+ // TODO: Handle Non-Uniform scale
+ // Currently this interpolator does not handle non uniform scale
+ // We cheat by just taking the x scale component
+
+ // Scale the transformation matrix
+ sMat.set((double)iScale.x);
+ tMat.mul(sMat);
+
+ // Set the translation components.
+ tMat.m03 = iPos.x;
+ tMat.m13 = iPos.y;
+ tMat.m23 = iPos.z;
+ rotation.set(tMat);
+
+ // construct a Transform3D from: axis * rotation * axisInverse
+ transform.mul(axis, rotation);
+ transform.mul(transform, axisInverse);
+ }
+
+ /**
+ * Copies KBRotPosScaleSplinePathInterpolator information from
+ * <code>originalNode</code> into
+ * the current node. This method is called from the
+ * <code>cloneNode</code> method which is, in turn, called by the
+ * <code>cloneTree</code> method.<P>
+ *
+ * @param forceDuplicate when set to <code>true</code>, causes the
+ * <code>duplicateOnCloneTree</code> flag to be ignored. When
+ * <code>false</code>, the value of each node's
+ * <code>duplicateOnCloneTree</code> variable determines whether
+ * NodeComponent data is duplicated or copied.
+ *
+ * @exception RestrictedAccessException if this object is part of a live
+ * or compiled scenegraph.
+ *
+ * @see Node#duplicateNode
+ * @see Node#cloneTree
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public Node cloneNode(boolean forceDuplicate) {
+ KBRotPosScaleSplinePathInterpolator spline =
+ new KBRotPosScaleSplinePathInterpolator();
+
+ spline.duplicateNode(this, forceDuplicate);
+ return spline;
+ }
+
+ /**
+ * Copies KBRotPosScaleSplinePathInterpolator information from
+ * <code>originalNode</code> into
+ * the current node. This method is called from the
+ * <code>cloneNode</code> method which is, in turn, called by the
+ * <code>cloneTree</code> method.<P>
+ *
+ * @param originalNode the original node to duplicate.
+ * @param forceDuplicate when set to <code>true</code>, causes the
+ * <code>duplicateOnCloneTree</code> flag to be ignored. When
+ * <code>false</code>, the value of each node's
+ * <code>duplicateOnCloneTree</code> variable determines whether
+ * NodeComponent data is duplicated or copied.
+ *
+ * @exception RestrictedAccessException if this object is part of a live
+ * or compiled scenegraph.
+ *
+ * @see Node#duplicateNode
+ * @see Node#cloneTree
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public void duplicateNode(Node originalNode, boolean forceDuplicate) {
+ super.duplicateNode(originalNode, forceDuplicate);
+
+ KBRotPosScaleSplinePathInterpolator interpolator =
+ (KBRotPosScaleSplinePathInterpolator)originalNode;
+ setAxisOfRotPosScale(interpolator.axis);
+ target = interpolator.target;
+ cubicSplineCurve = new KBCubicSplineCurve(interpolator.keyFrames);
+ numSegments = cubicSplineCurve.numSegments;
+ }
+}
+
+
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/KBSplinePathInterpolator.java b/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/KBSplinePathInterpolator.java
new file mode 100644
index 0000000..d67c5a9
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/KBSplinePathInterpolator.java
@@ -0,0 +1,301 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.interpolators;
+
+import javax.media.j3d.*;
+import java.util.*;
+import javax.vecmath.*;
+import com.sun.j3d.internal.J3dUtilsI18N;
+
+/**
+ * KBSplinePathInterpolator behavior. This class defines the base class for
+ * all Kochanek-Bartels (also known as TCB or Tension-Continuity-Bias)
+ * Spline Path Interpolators.
+ *
+ * @since Java3D 1.2
+ */
+
+public abstract class KBSplinePathInterpolator extends TransformInterpolator {
+
+ private int keysLength;
+ /**
+ * An array of KBKeyFrame for interpolator
+ */
+ protected KBKeyFrame[] keyFrames;
+
+ /**
+ * This value is the distance between knots
+ * value which can be used in further calculations by the subclass.
+ */
+ protected float currentU;
+
+ /**
+ * The lower knot
+ */
+ protected int lowerKnot;
+ /**
+ * The upper knot
+ */
+ protected int upperKnot;
+
+ /**
+ * Constructs a KBSplinePathInterpolator node with a null alpha value and
+ * a null target of TransformGroup
+ *
+ * @since Java 3D 1.3
+ */
+ KBSplinePathInterpolator() {
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>KBSplinePathInterpolator(Alpha, TransformGroup, TCBKeyFrame[]) </code>
+ */
+ public KBSplinePathInterpolator(Alpha alpha, KBKeyFrame keys[]) {
+ this(alpha, null, keys);
+ }
+
+ /**
+ * Constructs a new KBSplinePathInterpolator object that interpolates
+ * between keyframes with specified alpha, target and an default
+ * axisOfTranform set to identity.
+ * It takes at least two key frames. The first key
+ * frame's knot must have a value of 0.0 and the last knot must have a
+ * value of 1.0. An intermediate key frame with index k must have a
+ * knot value strictly greater than the knot value of a key frame with
+ * index less than k. Once this constructor has all the valid key frames
+ * it creates its own list of key fames that duplicates the first key frame
+ * at the beginning of the list and the last key frame at the end of the
+ * list.
+ * @param alpha the alpha object for this interpolator
+ * @param target the TransformGroup node affected by this interpolator
+ * @param keys an array of KBKeyFrame. Requires at least two key frames.
+ *
+ * @since Java 3D 1.3
+ */
+ public KBSplinePathInterpolator(Alpha alpha, TransformGroup target, KBKeyFrame keys[]) {
+ super(alpha, target);
+ processKeyFrames( keys );
+ }
+
+ /**
+ * Constructs a new KBSplinePathInterpolator object that interpolates
+ * between keyframes with specified alpha, target and axisOfTransform.
+ * It takes at least two key frames. The first key
+ * frame's knot must have a value of 0.0 and the last knot must have a
+ * value of 1.0. An intermediate key frame with index k must have a
+ * knot value strictly greater than the knot value of a key frame with
+ * index less than k. Once this constructor has all the valid key frames
+ * it creates its own list of key fames that duplicates the first key frame
+ * at the beginning of the list and the last key frame at the end of the
+ * list.
+ * @param alpha the alpha object for this interpolator
+ * @param target the TransformGroup node affected by this interpolator
+ * @param axisOfTransform the transform that defines the local coordinate
+ * @param keys an array of KBKeyFrame. Requires at least two key frames
+ *
+ * @since Java 3D 1.3
+ */
+ public KBSplinePathInterpolator(Alpha alpha,
+ TransformGroup target,
+ Transform3D axisOfTransform,
+ KBKeyFrame keys[]) {
+ super(alpha, target, axisOfTransform);
+ processKeyFrames( keys );
+ }
+
+ /**
+ * Process the new array of key frames
+ */
+ private void processKeyFrames( KBKeyFrame[] keys ) {
+
+ // Make sure that we have at least two key frames
+ keysLength = keys.length;
+ if (keysLength < 2) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("KBSplinePathInterpolator0"));
+
+ }
+
+ // Make sure that the first key frame's knot is equal to 0.0
+ if (keys[0].knot < -0.0001 || keys[0].knot > 0.0001) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("KBSplinePathInterpolator1"));
+ }
+
+ // Make sure that the last key frames knot is equal to 1.0
+ if (keys[keysLength-1].knot -1.0 < -0.0001 || keys[keysLength-1].knot -1.0 > 0.0001) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("KBSplinePathInterpolator2"));
+ }
+
+ // Make sure that all the knots are in sequence
+ for (int i = 0; i < keysLength; i++) {
+ if (i>0 && keys[i].knot < keys[i-1].knot) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("KBSplinePathInterpolator3"));
+ }
+ }
+
+ // Make space for a leading and trailing key frame in addition to
+ // the keys passed in
+ keyFrames = new KBKeyFrame[keysLength+2];
+ keyFrames[0] = new KBKeyFrame();
+ keyFrames[0] = keys[0];
+ for (int i = 1; i < keysLength+1; i++) {
+ keyFrames[i] = keys[i-1];
+ }
+ keyFrames[keysLength+1] = new KBKeyFrame();
+ keyFrames[keysLength+1] = keys[keysLength-1];
+
+ // Make key frame length reflect the 2 added key frames
+ keysLength += 2;
+ }
+
+ /**
+ * This method retrieves the length of the key frame array.
+ * @return the number of key frames
+ */
+ public int getArrayLength(){
+ return keysLength-2;
+ }
+
+ /**
+ * This method retrieves the key frame at the specified index.
+ * @param index the index of the key frame requested
+ * @return the key frame at the associated index
+ */
+ public KBKeyFrame getKeyFrame (int index) {
+
+ // We add 1 to index because we have added a leading keyFrame
+ return this.keyFrames[index+1];
+ }
+
+ /**
+ * Set the key frame at the specified index to <code>keyFrame</code>
+ * @param index Index of the key frame to change
+ * @param keyFrame The new key frame
+ * @since Java 3D 1.3
+ */
+ public void setKeyFrame( int index, KBKeyFrame keyFrame ) {
+ this.keyFrames[index+1] = keyFrame;
+ }
+
+ /**
+ * Set allthe key frames
+ * @param keyFrames The new key frame
+ * @since Java 3D 1.3
+ */
+ public void setKeyFrames( KBKeyFrame[] keyFrames ) {
+ processKeyFrames( keyFrames );
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>computePathInterpolation(float)</code>
+ */
+ protected void computePathInterpolation() {
+ computePathInterpolation(this.getAlpha().value());
+ }
+
+ /**
+ * This method computes the bounding knot indices and interpolation value
+ * "CurrentU" given the current value of the knots[] array and the
+ * specified alpha value
+ * @param alphaValue alpha value between 0.0 and 1.0
+ *
+ * @since Java 3D 1.3
+ */
+ protected void computePathInterpolation( float alphaValue ) {
+
+ // skip knots till we find the two we fall between
+ int i = 1;
+ int len = keysLength - 2;
+ while ((alphaValue > keyFrames[i].knot) && (i < len)) {
+ i++;
+ }
+
+ if (i == 1) {
+ currentU = 0f;
+ lowerKnot = 1;
+ upperKnot = 2;
+ } else {
+ currentU = (alphaValue - keyFrames[i-1].knot)/
+ (keyFrames[i].knot - keyFrames[i-1].knot);
+ lowerKnot = i-1;
+ upperKnot = i;
+ }
+ }
+
+ /**
+ * Copies all KBSplinePathInterpolator information from
+ * <code>originalNode</code> into
+ * the current node. This method is called from the
+ * <code>cloneNode</code> method which is, in turn, called by the
+ * <code>cloneTree</code> method.<P>
+ *
+ * @param originalNode the original node to duplicate.
+ * @param forceDuplicate when set to <code>true</code>, causes the
+ * <code>duplicateOnCloneTree</code> flag to be ignored. When
+ * <code>false</code>, the value of each node's
+ * <code>duplicateOnCloneTree</code> variable determines whether
+ * NodeComponent data is duplicated or copied.
+ *
+ * @exception RestrictedAccessException if this object is part of a live
+ * or compiled scenegraph.
+ *
+ * @see Node#duplicateNode
+ * @see Node#cloneTree
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public void duplicateNode(Node originalNode, boolean forceDuplicate) {
+ super.duplicateNode(originalNode, forceDuplicate);
+ KBSplinePathInterpolator originalSpline =
+ (KBSplinePathInterpolator) originalNode;
+ setAlpha(originalSpline.getAlpha());
+ keysLength = originalSpline.keysLength;
+ keyFrames = new KBKeyFrame[keysLength];
+ System.arraycopy(originalSpline.keyFrames, 0,
+ keyFrames, 0, keysLength);
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/RotPosScaleTCBSplinePathInterpolator.java b/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/RotPosScaleTCBSplinePathInterpolator.java
new file mode 100644
index 0000000..3367ae3
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/RotPosScaleTCBSplinePathInterpolator.java
@@ -0,0 +1,247 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.interpolators;
+
+import javax.media.j3d.*;
+import java.util.*;
+import javax.vecmath.*;
+
+
+/**
+ * RotPosScaleTCBSplinePathInterpolator behavior. This class defines a
+ * behavior that varies the rotational, translational, and scale components
+ * of its target TransformGroup by using the Kochanek-Bartels cubic spline
+ * interpolation to interpolate among a series of key frames
+ * (using the value generated by the specified Alpha object). The
+ * interpolated position, orientation, and scale are used to generate
+ * a transform in the local coordinate system of this interpolator.
+ *
+ * @since Java3D 1.1
+ */
+
+public class RotPosScaleTCBSplinePathInterpolator
+extends TCBSplinePathInterpolator {
+
+ private Transform3D rotation = new Transform3D();
+ private Matrix4d tMat = new Matrix4d();
+ private Matrix4d sMat = new Matrix4d();
+ private Quat4f iQuat = new Quat4f(); // interpolated quaternion
+ private Vector3f iPos = new Vector3f(); // interpolated position
+ private Point3f iScale = new Point3f(); // interpolated scale
+
+ CubicSplineCurve cubicSplineCurve = new CubicSplineCurve();
+ CubicSplineSegment cubicSplineSegments[];
+ int numSegments;
+ int currentSegmentIndex;
+ CubicSplineSegment currentSegment;
+
+ // non-public, default constructor used by cloneNode
+ RotPosScaleTCBSplinePathInterpolator() {
+ }
+
+ /**
+ * Constructs a new RotPosScaleTCBSplinePathInterpolator object that
+ * varies the rotation, translation, and scale of the target
+ * TransformGroup's transform. At least 2 key frames are required for
+ * this interpolator. The first key
+ * frame's knot must have a value of 0.0 and the last knot must have a
+ * value of 1.0. An intermediate key frame with index k must have a
+ * knot value strictly greater than the knot value of a key frame with
+ * index less than k.
+ * @param alpha the alpha object for this interpolator
+ * @param target the TransformGroup node affected by this interpolator
+ * @param axisOfTransform the transform that specifies the local
+ * coordinate system in which this interpolator operates.
+ * @param keys an array of key frames that defien the motion path
+ */
+ public RotPosScaleTCBSplinePathInterpolator(Alpha alpha,
+ TransformGroup target,
+ Transform3D axisOfTransform,
+ TCBKeyFrame keys[]) {
+ super(alpha, target, axisOfTransform, keys);
+ // Create a spline curve using the derived key frames
+ cubicSplineCurve = new CubicSplineCurve(this.keyFrames);
+ numSegments = cubicSplineCurve.numSegments;
+
+ }
+
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>TransformInterpolator.setTransformAxis(Transform3D)</code>
+ */
+ public void setAxisOfRotPosScale(Transform3D axisOfRotPosScale) {
+ setTransformAxis(axisOfRotPosScale);
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>TransformInterpolator.getTransformAxis()</code>
+ */
+ public Transform3D getAxisOfRotPosScale() {
+ return getTransformAxis();
+ }
+
+ /**
+ * Computes the new transform for this interpolator for a given
+ * alpha value.
+ *
+ * @param alphaValue alpha value between 0.0 and 1.0
+ * @param transform object that receives the computed transform for
+ * the specified alpha value
+ *
+ * @since Java 3D 1.3
+ */
+ public void computeTransform(float alphaValue, Transform3D transform) {
+
+ // compute the current value of u from alpha and the
+ // determine lower and upper knot points
+ computePathInterpolation(alphaValue);
+
+ // Determine the segment within which we will be interpolating
+ currentSegmentIndex = this.lowerKnot - 1;
+
+ // if we are at the start of the curve
+ if (currentSegmentIndex == 0 && currentU == 0f) {
+
+ iQuat.set(keyFrames[1].quat);
+ iPos.set(keyFrames[1].position);
+ iScale.set(keyFrames[1].scale);
+
+ // if we are at the end of the curve
+ } else if (currentSegmentIndex == (numSegments-1) &&
+ currentU == 1.0) {
+
+ iQuat.set(keyFrames[upperKnot].quat);
+ iPos.set(keyFrames[upperKnot].position);
+ iScale.set(keyFrames[upperKnot].scale);
+
+ // if we are somewhere in between the curve
+ } else {
+
+ // Get a reference to the current spline segment i.e. the
+ // one bounded by lowerKnot and upperKnot
+ currentSegment
+ = cubicSplineCurve.getSegment(currentSegmentIndex);
+
+ // interpolate quaternions
+ currentSegment.getInterpolatedQuaternion(currentU,iQuat);
+
+ // interpolate position
+ currentSegment.getInterpolatedPositionVector(currentU,iPos);
+
+ // interpolate position
+ currentSegment.getInterpolatedScale(currentU,iScale);
+
+ }
+
+ // Alway normalize the quaternion
+ iQuat.normalize();
+ tMat.set(iQuat);
+
+ // Set the translation components.
+ tMat.m03 = iPos.x;
+ tMat.m13 = iPos.y;
+ tMat.m23 = iPos.z;
+ rotation.set(tMat);
+
+ // construct a Transform3D from: axis * rotation * axisInverse
+ transform.mul(axis, rotation);
+ transform.setScale(new Vector3d(iScale));
+ transform.mul(transform, axisInverse);
+
+ }
+
+ /**
+ * Used to create a new instance of the node. This routine is called
+ * by <code>cloneTree</code> to duplicate the current node.
+ * @param forceDuplicate when set to <code>true</code>, causes the
+ * <code>duplicateOnCloneTree</code> flag to be ignored. When
+ * <code>false</code>, the value of each node's
+ * <code>duplicateOnCloneTree</code> variable determines whether
+ * NodeComponent data is duplicated or copied.
+ *
+ * @see Node#cloneTree
+ * @see Node#cloneNode
+ * @see Node#duplicateNode
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public Node cloneNode(boolean forceDuplicate) {
+ RotPosScaleTCBSplinePathInterpolator spline =
+ new RotPosScaleTCBSplinePathInterpolator();
+ spline.duplicateNode(this, forceDuplicate);
+ return spline;
+ }
+
+ /**
+ * Copies RotPosScaleTCBSplinePathInterpolator information from
+ * <code>originalNode</code> into
+ * the current node. This method is called from the
+ * <code>cloneNode</code> method which is, in turn, called by the
+ * <code>cloneTree</code> method.<P>
+ *
+ * @param originalNode the original node to duplicate.
+ * @param forceDuplicate when set to <code>true</code>, causes the
+ * <code>duplicateOnCloneTree</code> flag to be ignored. When
+ * <code>false</code>, the value of each node's
+ * <code>duplicateOnCloneTree</code> variable determines whether
+ * NodeComponent data is duplicated or copied.
+ *
+ * @exception RestrictedAccessException if this object is part of a live
+ * or compiled scenegraph.
+ *
+ * @see Node#duplicateNode
+ * @see Node#cloneTree
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public void duplicateNode(Node originalNode, boolean forceDuplicate) {
+ super.duplicateNode(originalNode, forceDuplicate);
+ RotPosScaleTCBSplinePathInterpolator interpolator =
+ (RotPosScaleTCBSplinePathInterpolator)originalNode;
+
+ cubicSplineCurve = new CubicSplineCurve(interpolator.keyFrames);
+ numSegments = cubicSplineCurve.numSegments;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/TCBKeyFrame.java b/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/TCBKeyFrame.java
new file mode 100644
index 0000000..39c7143
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/TCBKeyFrame.java
@@ -0,0 +1,144 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.interpolators;
+
+import javax.media.j3d.*;
+import java.util.*;
+import javax.vecmath.*;
+import com.sun.j3d.internal.J3dUtilsI18N;
+
+/**
+ * This class represents a Key Frame that can be used for Kochanek-Bartels
+ * (TCB) spline interpolation.
+ *
+ * @since Java3D 1.1
+ */
+
+public class TCBKeyFrame {
+
+ // Position, Rotation and Scale
+ public Point3f position;
+ public Quat4f quat;
+ public Point3f scale;
+
+ // Tension, Continuity & Bias
+ public float tension;
+ public float continuity;
+ public float bias;
+
+ // Sample Time
+ public float knot;
+
+ // Interpolation type (linear = 0 -> spline interpolation)
+ public int linear;
+
+ // default constructor
+ TCBKeyFrame () {
+ tension = continuity = bias = 0.0f;
+ }
+
+ public TCBKeyFrame (TCBKeyFrame kf) {
+ this(kf.knot, kf.linear, kf.position, kf.quat, kf.scale,
+ kf.tension, kf.continuity, kf.bias);
+
+ }
+
+ /**
+ * Creates a key frame using the given inputs.
+ *
+ * @param k knot value for this key frame
+ * @param l the linear flag (0 - Spline Interp, 1, Linear Interp
+ * @param pos the position at the key frame
+ * @param q the rotations at the key frame
+ * @param s the scales at the key frame
+ * @param t tension (-1.0 < t < 1.0)
+ * @param c continuity (-1.0 < c < 1.0)
+ * @param b bias (-1.0 < b < 1.0)
+ */
+ public TCBKeyFrame (float k, int l, Point3f pos, Quat4f q, Point3f s,
+ float t, float c, float b) {
+
+ knot = k;
+ linear = l;
+ position = new Point3f(pos);
+ quat = new Quat4f(q);
+ scale = new Point3f(s);
+
+ // Check for valid tension continuity and bias values
+ if (t < -1.0f || t > 1.0f) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("TCBKeyFrame0"));
+ }
+
+ if (b < -1.0f || b > 1.0f) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("TCBKeyFrame1"));
+ }
+
+ if (c < -1.0f || c > 1.0f) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("TCBKeyFrame2"));
+ }
+
+ // copy valid tension, continuity and bias values
+ tension = t;
+ continuity = c;
+ bias = b;
+ }
+
+ /**
+ * Prints information comtained in this key frame
+ * @param tag string tag for identifying debug message
+ */
+ public void debugPrint (String tag) {
+ System.out.println ("\n" + tag);
+ System.out.println (" knot = " + knot);
+ System.out.println (" linear = " + linear);
+ System.out.println (" position(x,y,z) = " + position.x + " "
+ + position.y + " " + position.z);
+
+ System.out.println (" tension = " + tension);
+ System.out.println (" continuity = " + continuity);
+ System.out.println (" bias = " + bias);
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/TCBSplinePathInterpolator.java b/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/TCBSplinePathInterpolator.java
new file mode 100644
index 0000000..adafb3b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/interpolators/TCBSplinePathInterpolator.java
@@ -0,0 +1,278 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.interpolators;
+
+import javax.media.j3d.*;
+import java.util.*;
+import javax.vecmath.*;
+import com.sun.j3d.internal.J3dUtilsI18N;
+
+/**
+ * TCBSplinePathInterpolator behavior. This class defines the base class for
+ * all TCB (Kochanek-Bartels) Spline Path Interpolators.
+ *
+ * @since Java3D 1.1
+ */
+
+public abstract class TCBSplinePathInterpolator extends TransformInterpolator {
+
+ private int keysLength;
+
+ /**
+ * An array of KBKeyFrame for interpolator
+ */
+ protected TCBKeyFrame[] keyFrames;
+
+ /**
+ * This value is the distance between knots
+ * value which can be used in further calculations by the subclass.
+ */
+ protected float currentU;
+
+ /**
+ * The lower knot
+ */
+ protected int lowerKnot;
+
+ /**
+ * The upper knot
+ */
+ protected int upperKnot;
+
+ /**
+ * Constructs a TCBSplinePathInterpolator node with a null alpha value and
+ * a null target of TransformGroup
+ *
+ * @since Java 3D 1.3
+ */
+
+ TCBSplinePathInterpolator() {
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>TCBSplinePathInterpolator(Alpha, TransformGroup, TCBKeyFrame[]) </code>
+ */
+ public TCBSplinePathInterpolator(Alpha alpha, TCBKeyFrame keys[]) {
+ this(alpha, null, keys);
+ }
+
+ /**
+ * Constructs a new TCBSplinePathInterpolator object that interpolates
+ * between keyframes with specified alpha, target and default axisOfTransform
+ * set to identity. It takes at least two key frames. The first key
+ * frame's knot must have a value of 0.0 and the last knot must have a
+ * value of 1.0. An intermediate key frame with index k must have a
+ * knot value strictly greater than the knot value of a key frame with
+ * index less than k. Once this constructor has all the valid key frames
+ * it creates its own list of key fames that duplicates the first key frame
+ * at the beginning of the list and the last key frame at the end of the
+ * list.
+ * @param alpha the alpha object for this interpolator
+ * @param target the TransformGroup node effected by this TCBSplinePathInterpolator
+ * @param keys an array of TCBKeyFrame. Requires at least two key frames.
+ *
+ * @since Java 3D 1.3
+ */
+ public TCBSplinePathInterpolator(Alpha alpha, TransformGroup target, TCBKeyFrame keys[]) {
+ super(alpha, target);
+ processKeyFrames(keys);
+ }
+
+ /**
+ * Constructs a new TCBSplinePathInterpolator object that interpolates
+ * between keyframes with specified alpha, target and axisOfTransform.
+ * It takes at least two key frames. The first key
+ * frame's knot must have a value of 0.0 and the last knot must have a
+ * value of 1.0. An intermediate key frame with index k must have a
+ * knot value strictly greater than the knot value of a key frame with
+ * index less than k. Once this constructor has all the valid key frames
+ * it creates its own list of key fames that duplicates the first key frame
+ * at the beginning of the list and the last key frame at the end of the
+ * list.
+ * @param alpha the alpha object for this interpolator
+ * @param target the TransformGroup node effected by this TCBSplinePathInterpolator
+ * @param axisOfTransform the transform that defines the local coordinate
+ * @param keys an array of TCBKeyFrame. Requires at least two key frames.
+ *
+ * @since Java 3D 1.3
+ */
+ public TCBSplinePathInterpolator(Alpha alpha, TransformGroup target, Transform3D axisOfTransform,
+ TCBKeyFrame keys[]) {
+ super(alpha, target, axisOfTransform);
+ processKeyFrames(keys);
+ }
+
+ /**
+ * Process the new array of key frames
+ */
+ private void processKeyFrames( TCBKeyFrame keys[] ){
+
+ // Make sure that we have at least two key frames
+ keysLength = keys.length;
+ if (keysLength < 2) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("TCBSplinePathInterpolator0"));
+
+ }
+
+ // Make sure that the first key frame's knot is equal to 0.0
+ if (keys[0].knot < -0.0001 || keys[0].knot > 0.0001) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("TCBSplinePathInterpolator1"));
+ }
+
+ // Make sure that the last key frames knot is equal to 1.0
+ if (keys[keysLength-1].knot -1.0 < -0.0001 || keys[keysLength-1].knot -1.0 > 0.0001) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("TCBSplinePathInterpolator2"));
+ }
+
+ // Make sure that all the knots are in sequence
+ for (int i = 0; i < keysLength; i++) {
+ if (i>0 && keys[i].knot < keys[i-1].knot) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("TCBSplinePathInterpolator3"));
+ }
+ }
+
+ // Make space for a leading and trailing key frame in addition to
+ // the keys passed in
+ keyFrames = new TCBKeyFrame[keysLength+2];
+ keyFrames[0] = new TCBKeyFrame();
+ keyFrames[0] = keys[0];
+ for (int i = 1; i < keysLength+1; i++) {
+ keyFrames[i] = keys[i-1];
+ }
+ keyFrames[keysLength+1] = new TCBKeyFrame();
+ keyFrames[keysLength+1] = keys[keysLength-1];
+
+ // Make key frame length reflect the 2 added key frames
+ keysLength += 2;
+ }
+
+ /**
+ * This method retrieves the length of the key frame array.
+ * @return the number of key frames
+ */
+ public int getArrayLength(){
+ return keysLength-2;
+ }
+
+ /**
+ * This method retrieves the key frame at the specified index.
+ * @param index the index of the key frame requested
+ * @return the key frame at the associated index
+ */
+ public TCBKeyFrame getKeyFrame (int index) {
+
+ // We add 1 to index because we have added a leading keyFrame
+ return this.keyFrames[index+1];
+ }
+
+ /**
+ * This method computes the bounding knot indices and interpolation value
+ * "CurrentU" given the specified value of alpha and the knots[] array.
+ * @param alphaValue alpha value between 0.0 and 1.0
+ *
+ * @since Java 3D 1.3
+ */
+ protected void computePathInterpolation(float alphaValue) {
+
+ // skip knots till we find the two we fall between
+ int i = 1;
+ int len = keysLength - 2;
+ while ((alphaValue > keyFrames[i].knot) && (i < len)) {
+ i++;
+ }
+
+ if (i == 1) {
+ currentU = 0f;
+ lowerKnot = 1;
+ upperKnot = 2;
+ } else {
+ currentU = (alphaValue - keyFrames[i-1].knot)/
+ (keyFrames[i].knot - keyFrames[i-1].knot);
+ lowerKnot = i-1;
+ upperKnot = i;
+ }
+ }
+
+ /**
+ * @deprecated As of Java 3D version 1.3, replaced by
+ * <code>computePathInterpolation(float)</code>
+ */
+ protected void computePathInterpolation() {
+ float value = (this.getAlpha()).value();
+ computePathInterpolation(value);
+ }
+
+ /**
+ * Copies all TCBSplinePathInterpolator information from
+ * <code>originalNode</code> into
+ * the current node. This method is called from the
+ * <code>cloneNode</code> method which is, in turn, called by the
+ * <code>cloneTree</code> method.<P>
+ *
+ * @param originalNode the original node to duplicate.
+ * @param forceDuplicate when set to <code>true</code>, causes the
+ * <code>duplicateOnCloneTree</code> flag to be ignored. When
+ * <code>false</code>, the value of each node's
+ * <code>duplicateOnCloneTree</code> variable determines whether
+ * NodeComponent data is duplicated or copied.
+ *
+ * @exception RestrictedAccessException if this object is part of a live
+ * or compiled scenegraph.
+ *
+ * @see Node#duplicateNode
+ * @see Node#cloneTree
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public void duplicateNode(Node originalNode, boolean forceDuplicate) {
+ super.duplicateNode(originalNode, forceDuplicate);
+ TCBSplinePathInterpolator originalSpline = (TCBSplinePathInterpolator) originalNode;
+ setAlpha(originalSpline.getAlpha());
+ keysLength = originalSpline.keysLength;
+ keyFrames = new TCBKeyFrame[keysLength];
+ System.arraycopy(originalSpline.keyFrames, 0,
+ keyFrames, 0, keysLength);
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/keyboard/KeyNavigator.java b/src/classes/share/com/sun/j3d/utils/behaviors/keyboard/KeyNavigator.java
new file mode 100644
index 0000000..d9b0f6c
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/keyboard/KeyNavigator.java
@@ -0,0 +1,499 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.keyboard;
+
+import java.io.*;
+import java.util.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import java.awt.event.*;
+
+/**
+ * This is the KeyNavigator class. It accumulates AWT key events (key
+ * press and key release) and computes a new transform based on the
+ * accumulated events and elapsed time.
+ */
+public class KeyNavigator {
+
+ private Vector3d navVec;
+ private long time;
+
+ private Vector3d fwdAcc;
+ private Vector3d bwdAcc;
+ private Vector3d leftAcc;
+ private Vector3d rightAcc;
+ private Vector3d upAcc;
+ private Vector3d downAcc;
+
+ private Vector3d fwdDrag;
+ private Vector3d bwdDrag;
+ private Vector3d leftDrag;
+ private Vector3d rightDrag;
+ private Vector3d upDrag;
+ private Vector3d downDrag;
+
+ private double fwdVMax;
+ private double bwdVMax;
+ private double leftVMax;
+ private double rightVMax;
+ private double upVMax;
+ private double downVMax;
+
+ private float leftRotAngle;
+ private float rightRotAngle;
+ private float upRotAngle;
+ private float downRotAngle;
+
+ private double mmx;
+
+ private Vector3d a = new Vector3d();
+ private Vector3d dv = new Vector3d();
+ private Point3d dp = new Point3d();
+ private Quat4d udQuat = new Quat4d();
+ private Quat4d lrQuat = new Quat4d();
+ private Vector3d vpPos = new Vector3d();
+ private double vpScale;
+ private Quat4d vpQuat = new Quat4d();
+ private Matrix4d vpMatrix = new Matrix4d();
+ private Transform3D vpTrans = new Transform3D();
+ private Matrix4d mat = new Matrix4d();
+ private Vector3d nda = new Vector3d();
+ private Vector3d temp = new Vector3d();
+ private Transform3D nominal = new Transform3D();
+ private TransformGroup targetTG;
+
+ private static final int UP_ARROW = (1<<0);
+ private static final int DOWN_ARROW = (1<<1);
+ private static final int LEFT_ARROW = (1<<2);
+ private static final int RIGHT_ARROW = (1<<3);
+ private static final int PLUS_SIGN = (1<<4);
+ private static final int MINUS_SIGN = (1<<5);
+ private static final int PAGE_UP = (1<<6);
+ private static final int PAGE_DOWN = (1<<7);
+ private static final int HOME_DIR = (1<<8);
+ private static final int HOME_NOMINAL = (1<<9);
+
+ private static final int SHIFT = (1<<10);
+ private static final int ALT = (1<<11);
+ private static final int META = (1<<12);
+
+ private static final int KEY_UP = (1<<13);
+ private static final int KEY_DOWN = (1<<14);
+
+ private int key_state = 0;
+ private int modifier_key_state = 0;
+
+
+ /**
+ * Constructs a new key navigator object that operates on the specified
+ * transform group. All parameters are set to their default, idle state.
+ * @param targetTG the target transform group
+ */
+ public KeyNavigator(TransformGroup targetTG) {
+ this.targetTG = targetTG;
+ targetTG.getTransform(nominal);
+
+ mmx = 128.0;
+ navVec = new Vector3d(0.0,0.0,0.0);
+
+ fwdAcc = new Vector3d( 0.0, 0.0,-mmx);
+ bwdAcc = new Vector3d( 0.0, 0.0, mmx);
+ leftAcc = new Vector3d(-mmx, 0.0, 0.0);
+ rightAcc = new Vector3d( mmx, 0.0, 0.0);
+ upAcc = new Vector3d( 0.0, mmx, 0.0);
+ downAcc = new Vector3d( 0.0,-mmx, 0.0);
+
+ fwdDrag = new Vector3d( 0.0, 0.0, mmx);
+ bwdDrag = new Vector3d( 0.0, 0.0,-mmx);
+ leftDrag = new Vector3d( mmx, 0.0, 0.0);
+ rightDrag = new Vector3d(-mmx, 0.0, 0.0);
+ upDrag = new Vector3d( 0.0,-mmx, 0.0);
+ downDrag = new Vector3d( 0.0, mmx, 0.0);
+
+ fwdVMax = -mmx;
+ bwdVMax = mmx;
+ leftVMax = -mmx;
+ rightVMax = mmx;
+ upVMax = mmx;
+ downVMax = -mmx;
+
+ leftRotAngle = (float) (-Math.PI*2.0/3.0);
+ rightRotAngle = (float) (Math.PI*2.0/3.0);
+ upRotAngle = (float) (Math.PI*2.0/3.0);
+ downRotAngle = (float) (-Math.PI*2.0/3.0);
+
+ // Create Timer here.
+ time = System.currentTimeMillis();
+
+ }
+
+
+ private long getDeltaTime() {
+ long newTime = System.currentTimeMillis();
+ long deltaTime = newTime - time;
+ time = newTime;
+ if (deltaTime > 2000) return 0;
+ else return deltaTime;
+ }
+
+ /* Generate a quaterion as a rotation of radians av about 0/x 1/y 2/z axis */
+ private void genRotQuat(double av, int axis, Quat4d q) {
+ double b;
+
+ q.x = q.y = q.z = 0.0;
+ q.w = Math.cos(av/2.0);
+
+ b = 1.0 - q.w*q.w;
+
+ if (b > 0.0)
+ b = Math.sqrt(b);
+ else
+ return;
+
+ if (av < 0.0)
+ b = -b;
+ if (axis == 0)
+ q.x = b;
+ else if (axis == 1)
+ q.y = b;
+ else
+ q.z = b;
+
+ }
+
+ private void accKeyAdd(Vector3d a, Vector3d da, Vector3d drag, double scaleVel) {
+
+ /* Scaling of acceleration due to modification keys */
+ nda.scale(scaleVel, da);
+ /* Addition of sufficent acceleration to counteract drag */
+ nda.sub(drag);
+
+ /* Summing into overall acceleration */
+ a.add(nda);
+
+ }
+
+
+ /**
+ * Computes a new transform for the next frame based on
+ * the current transform, accumulated keyboard inputs, and
+ * elapsed time. This new transform is written into the target
+ * transform group.
+ * This method should be called once per frame.
+ */
+ public void integrateTransformChanges() {
+ double scaleVel, scaleRot, scaleScale, pre;
+ double udAng, lrAng, r;
+
+ // Get the current View Platform transform into a transform3D object.
+ targetTG.getTransform(vpTrans);
+ // Extract the position, quaterion, and scale from the transform3D.
+ vpScale = vpTrans.get(vpQuat, vpPos);
+
+
+ double deltaTime = (double) getDeltaTime();
+ deltaTime *= 0.001;
+
+ /* Calculate scale due to modification keys */
+ if ((modifier_key_state & SHIFT) != 0 &&
+ (modifier_key_state & META) == 0) {
+ scaleVel = 3.0; scaleRot = 2.0; scaleScale = 4.0;
+ }
+ else if ((modifier_key_state & SHIFT) == 0 &&
+ (modifier_key_state & META) != 0) {
+ scaleVel = 0.1; scaleRot = 0.1; scaleScale = 0.1;
+ }
+ else if ((modifier_key_state & SHIFT) != 0 &&
+ (modifier_key_state & META) != 0) {
+ scaleVel = 0.3; scaleRot = 0.5; scaleScale = 0.1;
+ }
+ else {
+ scaleRot = scaleVel = 1.0; scaleScale = 4.0;
+ }
+
+ /*
+ * Processing of rectiliear motion keys.
+ */
+
+ a.x = a.y = a.z = 0.0; /* acceleration initially 0 */
+
+ /* Acceleration due to keys being down */
+ if ((key_state & UP_ARROW) != 0 && (key_state & DOWN_ARROW) == 0)
+ accKeyAdd(a, fwdAcc, fwdDrag, scaleVel);
+ else
+ if ((key_state & UP_ARROW) == 0 && (key_state & DOWN_ARROW) != 0)
+ accKeyAdd(a, bwdAcc, bwdDrag, scaleVel);
+
+ if (((modifier_key_state & ALT) != 0) &&
+ (key_state & LEFT_ARROW) != 0 && (key_state & RIGHT_ARROW) == 0) {
+ accKeyAdd(a, leftAcc, leftDrag, scaleVel);
+ } else
+ if (((modifier_key_state & ALT) != 0) &&
+ (key_state & LEFT_ARROW) == 0 && (key_state & RIGHT_ARROW) != 0)
+ accKeyAdd(a, rightAcc, rightDrag, scaleVel);
+
+ if (((modifier_key_state & ALT) != 0) &&
+ (key_state & PAGE_UP) != 0 && (key_state & PAGE_DOWN) == 0)
+ accKeyAdd(a, upAcc, upDrag, scaleVel);
+ else
+ if (((modifier_key_state & ALT) != 0) &&
+ (key_state & PAGE_UP) == 0 && (key_state & PAGE_DOWN) != 0)
+ accKeyAdd(a, downAcc, downDrag, scaleVel);
+
+
+ /*
+ * Drag due to new or existing motion
+ */
+ pre = navVec.z + a.z * deltaTime;
+ if (pre < 0.0) {
+ if (pre + fwdDrag.z * deltaTime < 0.0)
+ a.add(fwdDrag);
+ else
+ a.z -= pre/deltaTime;
+ } else if (pre > 0.0) {
+ if (pre + bwdDrag.z * deltaTime > 0.0)
+ a.add(bwdDrag);
+ else
+ a.z -= pre/deltaTime;
+ }
+
+ pre = navVec.x + a.x * deltaTime;
+ if (pre < 0.0) {
+ if (pre + leftDrag.x * deltaTime < 0.0)
+ a.add(leftDrag);
+ else
+ a.x -= pre/deltaTime;
+ } else if (pre > 0.0) {
+ if (pre + rightDrag.x * deltaTime > 0.0)
+ a.add(rightDrag);
+ else
+ a.x -= pre/deltaTime;
+ }
+
+ pre = navVec.y + a.y * deltaTime;
+ if (pre < 0.0) {
+ if (pre + downDrag.y * deltaTime < 0.0)
+ a.add(downDrag);
+ else
+ a.y -= pre/deltaTime;
+ } else if (pre > 0.0) {
+ if (pre + upDrag.y * deltaTime > 0.0)
+ a.add(upDrag);
+ else
+ a.y -= pre/deltaTime;
+ }
+
+ /* Integration of acceleration to velocity */
+ dv.scale(deltaTime, a);
+ navVec.add(dv);
+
+ /* Speed limits */
+ if (navVec.z < scaleVel * fwdVMax) navVec.z = scaleVel * fwdVMax;
+ if (navVec.z > scaleVel * bwdVMax) navVec.z = scaleVel * bwdVMax;
+ if (navVec.x < scaleVel * leftVMax) navVec.x = scaleVel * leftVMax;
+ if (navVec.x > scaleVel * rightVMax) navVec.x = scaleVel* rightVMax;
+ if (navVec.y > scaleVel * upVMax) navVec.y = scaleVel * upVMax;
+ if (navVec.y < scaleVel * downVMax) navVec.y = scaleVel * downVMax;
+
+ /* Integration of velocity to distance */
+ dp.scale(deltaTime, navVec);
+
+ /* Scale our motion to the current avatar scale */
+ // 1.0 eventually needs to be a more complex value (see hs).
+ // r = workplace_coexistence_to_vworld_ori.scale/
+ // one_to_one_coexistence_to_vworld_ori.scale;
+ r = vpScale/1.0;
+ dp.scale(r, dp);
+
+ /*
+ * Processing of rotation motion keys.
+ */
+ udAng = lrAng = 0.0;
+
+ /* Rotation due to keys being down */
+ if (((modifier_key_state & ALT) == 0) &&
+ (key_state & LEFT_ARROW) != 0 && (key_state & RIGHT_ARROW) == 0)
+ lrAng = (double) leftRotAngle;
+ else if (((modifier_key_state & ALT) == 0) &&
+ (key_state & LEFT_ARROW) == 0 && (key_state & RIGHT_ARROW) != 0)
+ lrAng = (double) rightRotAngle;
+
+ if (((modifier_key_state & ALT) == 0) &&
+ (key_state & PAGE_UP) != 0 && (key_state & PAGE_DOWN) == 0)
+ udAng = (double) upRotAngle;
+ else if (((modifier_key_state & ALT) == 0) &&
+ (key_state & PAGE_UP) == 0 && (key_state & PAGE_DOWN) != 0)
+ udAng = (double) downRotAngle;
+
+ lrAng *= scaleRot;
+ udAng *= scaleRot;
+
+ /* Scaling of angle change to delta time */
+ lrAng *= deltaTime;
+ udAng *= deltaTime;
+
+
+ /* Addition to existing orientation */
+ // vr_quat_inverse(&workplace_coexistence_to_vworld_ori.quat, &vpQuat);
+ // vpQuat gotten at top of method.
+ vpQuat.inverse();
+
+ if(lrAng != 0.0) {
+ genRotQuat(lrAng, 1, lrQuat);
+ vpQuat.mul(lrQuat, vpQuat);
+ }
+
+ if(udAng != 0.0) {
+ genRotQuat(udAng, 0, udQuat);
+ vpQuat.mul(udQuat, vpQuat);
+ }
+
+ /* Rotation of distance vector */
+ vpQuat.inverse();
+ vpQuat.normalize(); /* Improvment over HoloSketch */
+ mat.set(vpQuat);
+ mat.transform(dp);
+
+ /* Processing of scale */
+ if ((key_state & PLUS_SIGN) != 0) {
+ vpScale *= (1.0 + (scaleScale*deltaTime));
+ if (vpScale > 10e+14) vpScale = 1.0;
+ } else if ((key_state & MINUS_SIGN) != 0) {
+ vpScale /= (1.0 + (scaleScale*deltaTime));
+ if (vpScale < 10e-14) vpScale = 1.0;
+ }
+
+ // add dp into current vp position.
+ vpPos.add(dp);
+
+ if ((key_state & HOME_NOMINAL) != 0) {
+ resetVelocity();
+ // Extract the position, quaterion, and scale from the nominal
+ // transform
+ vpScale = nominal.get(vpQuat, vpPos);
+ }
+
+
+ /* Final update of view platform */
+ // Put the transform back into the transform group.
+ vpTrans.set(vpQuat, vpPos, vpScale);
+ targetTG.setTransform(vpTrans);
+ }
+
+
+ /**
+ * Resets the keyboard navigation velocity to 0.
+ */
+ private void resetVelocity() {
+ navVec.x = navVec.y = navVec.z = 0.0;
+ }
+
+
+ /**
+ * Processed a keyboard event. This routine should be called
+ * every time a KEY_PRESSED or KEY_RELEASED event is received.
+ * @param keyEvent the AWT key event
+ */
+ public void processKeyEvent(KeyEvent keyEvent) {
+ int keyCode = keyEvent.getKeyCode();
+ int keyChar = keyEvent.getKeyChar();
+
+//System.err.println("keyCode " + keyCode + " keyChar " + keyChar);
+
+ if (keyEvent.getID() == KeyEvent.KEY_RELEASED) {
+ if (keyChar == '+') key_state &= ~PLUS_SIGN;
+ else
+ switch (keyCode) {
+ case KeyEvent.VK_UP: key_state &= ~UP_ARROW; break;
+ case KeyEvent.VK_DOWN: key_state &= ~DOWN_ARROW; break;
+ case KeyEvent.VK_LEFT: key_state &= ~LEFT_ARROW; break;
+ case KeyEvent.VK_RIGHT: key_state &= ~RIGHT_ARROW; break;
+ case KeyEvent.VK_PAGE_UP: key_state &= ~PAGE_UP; break;
+ case KeyEvent.VK_PAGE_DOWN: key_state &= ~PAGE_DOWN; break;
+ case KeyEvent.VK_EQUALS: key_state &= ~HOME_NOMINAL;break;
+ default: switch(keyChar) {
+ case '-': key_state &= ~MINUS_SIGN; break;
+ }
+ }
+ } else if (keyEvent.getID() == KeyEvent.KEY_PRESSED) {
+ if (keyChar == '+') key_state |= PLUS_SIGN;
+ switch (keyCode) {
+ case KeyEvent.VK_UP: key_state |= UP_ARROW; break;
+ case KeyEvent.VK_DOWN: key_state |= DOWN_ARROW; break;
+ case KeyEvent.VK_LEFT: key_state |= LEFT_ARROW; break;
+ case KeyEvent.VK_RIGHT: key_state |= RIGHT_ARROW; break;
+ case KeyEvent.VK_PAGE_UP: key_state |= PAGE_UP; break;
+ case KeyEvent.VK_PAGE_DOWN: key_state |= PAGE_DOWN; break;
+ case KeyEvent.VK_EQUALS: key_state |= HOME_NOMINAL;break;
+ default: switch(keyChar) {
+ case '-': key_state |= MINUS_SIGN; break;
+ }
+ }
+ }
+
+ /* Check modifier keys */
+ if (keyEvent.isShiftDown())
+ modifier_key_state |= SHIFT;
+ else
+ modifier_key_state &= ~SHIFT;
+
+ if (keyEvent.isMetaDown())
+ modifier_key_state |= META;
+ else
+ modifier_key_state &= ~META;
+
+ if (keyEvent.isAltDown())
+ modifier_key_state |= ALT;
+ else
+ modifier_key_state &= ~ALT;
+
+//System.err.println("keyCode " + keyEvent.getKeyCode() + " modifiers " + keyEvent.getModifiers());
+//System.err.println("SHIFT_MASK " + keyEvent.SHIFT_MASK);
+//System.err.println("CTRL_MASK " + keyEvent.CTRL_MASK);
+//System.err.println("META_MASK " + keyEvent.META_MASK);
+//System.err.println("ALT_MASK " + keyEvent.ALT_MASK);
+
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/keyboard/KeyNavigatorBehavior.java b/src/classes/share/com/sun/j3d/utils/behaviors/keyboard/KeyNavigatorBehavior.java
new file mode 100644
index 0000000..226fe6b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/keyboard/KeyNavigatorBehavior.java
@@ -0,0 +1,216 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.keyboard;
+
+import java.awt.event.*;
+import java.awt.AWTEvent;
+import java.util.Enumeration;
+import java.awt.Component;
+import java.util.LinkedList;
+import javax.vecmath.*;
+import javax.media.j3d.*;
+import com.sun.j3d.internal.J3dUtilsI18N;
+
+/**
+ * This class is a simple behavior that invokes the KeyNavigator
+ * to modify the view platform transform.
+ */
+public class KeyNavigatorBehavior extends Behavior implements KeyListener {
+ private WakeupCriterion w1 = new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED);
+ private WakeupCriterion w2 = new WakeupOnAWTEvent(KeyEvent.KEY_RELEASED);
+ private WakeupOnElapsedFrames w3 = new WakeupOnElapsedFrames(0);
+ private WakeupCriterion[] warray = { w1, w2, w3 };
+ private WakeupCondition w = new WakeupOr(warray);
+ private KeyEvent eventKey;
+ private KeyNavigator keyNavigator;
+ private boolean listener = false;
+
+ private LinkedList eventq;
+
+
+ /**
+ * Override Behavior's initialize method to setup wakeup criteria.
+ */
+ public void initialize() {
+ // Establish initial wakeup criteria
+ if (listener) {
+ w1 = new WakeupOnBehaviorPost(this, KeyEvent.KEY_PRESSED);
+ w2 = new WakeupOnBehaviorPost(this, KeyEvent.KEY_RELEASED);
+ warray[0] = w1;
+ warray[1] = w2;
+ w = new WakeupOr(warray);
+ eventq = new LinkedList();
+ }
+ wakeupOn(w);
+ }
+
+ /**
+ * Override Behavior's stimulus method to handle the event.
+ */
+ public void processStimulus(Enumeration criteria) {
+ WakeupOnAWTEvent ev;
+ WakeupCriterion genericEvt;
+ AWTEvent[] events;
+ boolean sawFrame = false;
+
+ while (criteria.hasMoreElements()) {
+ genericEvt = (WakeupCriterion) criteria.nextElement();
+ if (genericEvt instanceof WakeupOnAWTEvent) {
+ ev = (WakeupOnAWTEvent) genericEvt;
+ events = ev.getAWTEvent();
+ processAWTEvent(events);
+ } else if (genericEvt instanceof WakeupOnElapsedFrames &&
+ eventKey != null) {
+ sawFrame = true;
+ } else if ((genericEvt instanceof WakeupOnBehaviorPost)) {
+ while(true) {
+ // access to the queue must be synchronized
+ synchronized (eventq) {
+ if (eventq.isEmpty()) break;
+ eventKey = (KeyEvent)eventq.remove(0);
+ if (eventKey.getID() == KeyEvent.KEY_PRESSED ||
+ eventKey.getID() == KeyEvent.KEY_RELEASED) {
+ keyNavigator.processKeyEvent(eventKey);
+ }
+ }
+ }
+ }
+ }
+ if (sawFrame)
+ keyNavigator.integrateTransformChanges();
+
+ // Set wakeup criteria for next time
+ wakeupOn(w);
+ }
+
+ /**
+ * Process a keyboard event
+ */
+ private void processAWTEvent(AWTEvent[] events) {
+ for (int loop = 0; loop < events.length; loop++) {
+ if (events[loop] instanceof KeyEvent) {
+ eventKey = (KeyEvent) events[loop];
+ // change the transformation; for example to zoom
+ if (eventKey.getID() == KeyEvent.KEY_PRESSED ||
+ eventKey.getID() == KeyEvent.KEY_RELEASED) {
+ //System.out.println("Keyboard is hit! " + eventKey);
+ keyNavigator.processKeyEvent(eventKey);
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds this behavior as a KeyListener to the specified component.
+ * This method can only be called if
+ * the behavior was created with one of the constructors that takes
+ * a Component as a parameter.
+ * @param c The component to add the KeyListener to.
+ * @exception IllegalStateException if the behavior was not created
+ * as a listener
+ * @since Java 3D 1.2.1
+ */
+ public void addListener(Component c) {
+ if (!listener) {
+ throw new IllegalStateException(J3dUtilsI18N.getString("Behavior0"));
+ }
+ c.addKeyListener(this);
+ }
+
+ /**
+ * Constructs a new key navigator behavior node that operates
+ * on the specified transform group.
+ * @param targetTG the target transform group
+ */
+ public KeyNavigatorBehavior(TransformGroup targetTG) {
+ keyNavigator = new KeyNavigator(targetTG);
+ }
+
+ /**
+ * Constructs a key navigator behavior that uses AWT listeners
+ * and behavior posts rather than WakeupOnAWTEvent. The behavior
+ * is added to the specified Component and works on the given
+ * TransformGroup. A null component can be passed to specify
+ * the behavior should use listeners. Components can then be added
+ * to the behavior with the addListener(Component c) method.
+ * @param c The component to add the KeyListener to.
+ * @param targetTG The target transform group.
+ * @since Java 3D 1.2.1
+ */
+ public KeyNavigatorBehavior(Component c, TransformGroup targetTG) {
+ this(targetTG);
+ if (c != null) {
+ c.addKeyListener(this);
+ }
+ listener = true;
+ }
+
+ public void keyPressed(KeyEvent evt) {
+// System.out.println("keyPressed");
+
+ // add new event to the queue
+ // must be MT safe
+ synchronized (eventq) {
+ eventq.add(evt);
+ // only need to post if this is the only event in the queue
+ if (eventq.size() == 1) postId(KeyEvent.KEY_PRESSED);
+ }
+ }
+
+ public void keyReleased(KeyEvent evt) {
+// System.out.println("keyReleased");
+
+ // add new event to the queue
+ // must be MT safe
+ synchronized (eventq) {
+ eventq.add(evt);
+ // only need to post if this is the only event in the queue
+ if (eventq.size() == 1) postId(KeyEvent.KEY_RELEASED);
+ }
+ }
+
+ public void keyTyped(KeyEvent evt) {}
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/mouse/MouseBehavior.java b/src/classes/share/com/sun/j3d/utils/behaviors/mouse/MouseBehavior.java
new file mode 100644
index 0000000..20e8f08
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/mouse/MouseBehavior.java
@@ -0,0 +1,329 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.mouse;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import com.sun.j3d.internal.J3dUtilsI18N;
+
+
+/**
+ * Base class for all mouse manipulators (see MouseRotate, MouseZoom
+ * and MouseTranslate for
+ * examples of how to extend this base class).
+ */
+
+public abstract class MouseBehavior extends Behavior
+ implements MouseListener, MouseMotionListener {
+
+ private boolean listener = false;
+
+ protected WakeupCriterion[] mouseEvents;
+ protected WakeupOr mouseCriterion;
+ protected int x, y;
+ protected int x_last, y_last;
+ protected TransformGroup transformGroup;
+ protected Transform3D transformX;
+ protected Transform3D transformY;
+ protected Transform3D currXform;
+ protected boolean buttonPress = false;
+ protected boolean reset = false;
+ protected boolean invert = false;
+ protected boolean wakeUp = false;
+ protected int flags = 0;
+
+ // to queue the mouse events
+ protected LinkedList mouseq;
+
+ // true if this behavior is enable
+ protected boolean enable = true;
+
+ /**
+ * Set this flag if you want to manually wakeup the behavior.
+ */
+ public static final int MANUAL_WAKEUP = 0x1;
+
+ /**
+ * Set this flag if you want to invert the inputs. This is useful when
+ * the transform for the view platform is being changed instead of the
+ * transform for the object.
+ */
+ public static final int INVERT_INPUT = 0x2;
+
+ /**
+ * Creates a mouse behavior object with a given transform group.
+ * @param transformGroup The transform group to be manipulated.
+ */
+ public MouseBehavior(TransformGroup transformGroup) {
+ super();
+ // need to remove old behavior from group
+ this.transformGroup = transformGroup;
+ currXform = new Transform3D();
+ transformX = new Transform3D();
+ transformY = new Transform3D();
+ reset = true;
+ }
+
+ /**
+ * Initializes standard fields. Note that this behavior still
+ * needs a transform group to work on (use setTransformGroup(tg)) and
+ * the transform group must add this behavior.
+ * @param format flags
+ */
+ public MouseBehavior(int format) {
+ super();
+ flags = format;
+ currXform = new Transform3D();
+ transformX = new Transform3D();
+ transformY = new Transform3D();
+ reset = true;
+ }
+
+ /**
+ * Creates a mouse behavior that uses AWT listeners and behavior
+ * posts rather than WakeupOnAWTEvent. The behaviors is added to
+ * the specified Component and works on the given TransformGroup.
+ * A null component can be passed to specify the behaviors should use
+ * listeners. Components can then be added to the behavior with the
+ * addListener(Component c) method.
+ * @param c The Component to add the MouseListener and
+ * MouseMotionListener to.
+ * @param transformGroup The TransformGroup to operate on.
+ * @since Java 3D 1.2.1
+ */
+ public MouseBehavior(Component c, TransformGroup transformGroup) {
+ this(transformGroup);
+ if (c != null) {
+ c.addMouseListener(this);
+ c.addMouseMotionListener(this);
+ }
+ listener = true;
+ }
+
+ /**
+ * Creates a mouse behavior that uses AWT listeners and behavior
+ * posts rather than WakeupOnAWTEvent. The behavior is added to the
+ * specified Component. A null component can be passed to specify
+ * the behavior should use listeners. Components can then be added
+ * to the behavior with the addListener(Component c) method.
+ * Note that this behavior still needs a transform
+ * group to work on (use setTransformGroup(tg)) and the transform
+ * group must add this behavior.
+ * @param format interesting flags (wakeup conditions).
+ * @since Java 3D 1.2.1
+ */
+ public MouseBehavior(Component c, int format) {
+ this(format);
+ if (c != null) {
+ c.addMouseListener(this);
+ c.addMouseMotionListener(this);
+ }
+ listener = true;
+ }
+
+ /**
+ * Swap a new transformGroup replacing the old one. This allows
+ * manipulators to operate on different nodes.
+ *
+ * @param transformGroup The *new* transform group to be manipulated.
+ */
+ public void setTransformGroup(TransformGroup transformGroup){
+ // need to remove old behavior from group
+ this.transformGroup = transformGroup;
+ currXform = new Transform3D();
+ transformX = new Transform3D();
+ transformY = new Transform3D();
+ reset = true;
+ }
+
+ /**
+ * Return the transformGroup on which this node is operating
+ */
+ public TransformGroup getTransformGroup() {
+ return this.transformGroup;
+ }
+
+ /** Initializes the behavior.
+ */
+
+ public void initialize() {
+ mouseEvents = new WakeupCriterion[3];
+ if (!listener) {
+ mouseEvents[0] = new WakeupOnAWTEvent(MouseEvent.MOUSE_DRAGGED);
+ mouseEvents[1] = new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED);
+ mouseEvents[2] = new WakeupOnAWTEvent(MouseEvent.MOUSE_RELEASED);
+ }
+ else {
+ mouseEvents[0] = new WakeupOnBehaviorPost(this,
+ MouseEvent.MOUSE_DRAGGED);
+ mouseEvents[1] = new WakeupOnBehaviorPost(this,
+ MouseEvent.MOUSE_PRESSED);
+ mouseEvents[2] = new WakeupOnBehaviorPost(this,
+ MouseEvent.MOUSE_RELEASED);
+ mouseq = new LinkedList();
+ }
+ mouseCriterion = new WakeupOr(mouseEvents);
+ wakeupOn (mouseCriterion);
+ x = 0;
+ y = 0;
+ x_last = 0;
+ y_last = 0;
+ }
+
+ /**
+ * Manually wake up the behavior. If MANUAL_WAKEUP flag was set upon
+ * creation, you must wake up this behavior each time it is handled.
+ */
+
+ public void wakeup()
+ {
+ wakeUp = true;
+ }
+
+ /**
+ * Handles mouse events
+ */
+ public void processMouseEvent(MouseEvent evt) {
+ if (evt.getID()==MouseEvent.MOUSE_PRESSED) {
+ buttonPress = true;
+ return;
+ }
+ else if (evt.getID()==MouseEvent.MOUSE_RELEASED){
+ buttonPress = false;
+ wakeUp = false;
+ }
+ else if (evt.getID() == MouseEvent.MOUSE_MOVED) {
+ // Process mouse move event
+ }
+ }
+
+ /**
+ * All mouse manipulators must implement this.
+ */
+ public abstract void processStimulus (Enumeration criteria);
+
+ /**
+ * Adds this behavior as a MouseListener and MouseMotionListener to
+ * the specified component. This method can only be called if
+ * the behavior was created with one of the constructors that takes
+ * a Component as a parameter.
+ * @param c The component to add the MouseListener and
+ * MouseMotionListener to.
+ * @exception IllegalStateException if the behavior was not created
+ * as a listener
+ * @since Java 3D 1.2.1
+ */
+ public void addListener(Component c) {
+ if (!listener) {
+ throw new IllegalStateException(J3dUtilsI18N.getString("Behavior0"));
+ }
+ c.addMouseListener(this);
+ c.addMouseMotionListener(this);
+ }
+
+ public void mouseClicked(MouseEvent e) {}
+ public void mouseEntered(MouseEvent e) {}
+ public void mouseExited(MouseEvent e) {}
+
+ public void mousePressed(MouseEvent e) {
+// System.out.println("mousePressed");
+
+ // add new event to the queue
+ // must be MT safe
+ if (enable) {
+ synchronized (mouseq) {
+ mouseq.add(e);
+ // only need to post if this is the only event in the queue
+ if (mouseq.size() == 1)
+ postId(MouseEvent.MOUSE_PRESSED);
+ }
+ }
+ }
+
+ public void mouseReleased(MouseEvent e) {
+// System.out.println("mouseReleased");
+
+ // add new event to the queue
+ // must be MT safe
+ if (enable) {
+ synchronized (mouseq) {
+ mouseq.add(e);
+ // only need to post if this is the only event in the queue
+ if (mouseq.size() == 1)
+ postId(MouseEvent.MOUSE_RELEASED);
+ }
+ }
+ }
+
+ public void mouseDragged(MouseEvent e) {
+// System.out.println("mouseDragged");
+
+ // add new event to the to the queue
+ // must be MT safe.
+ if (enable) {
+ synchronized (mouseq) {
+ mouseq.add(e);
+ // only need to post if this is the only event in the queue
+ if (mouseq.size() == 1)
+ postId(MouseEvent.MOUSE_DRAGGED);
+ }
+ }
+ }
+
+ public void mouseMoved(MouseEvent e) {}
+
+ public void setEnable(boolean state) {
+ super.setEnable(state);
+ this.enable = state;
+ if (!enable && (mouseq != null)) {
+ mouseq.clear();
+ }
+ }
+}
+
+
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/mouse/MouseBehaviorCallback.java b/src/classes/share/com/sun/j3d/utils/behaviors/mouse/MouseBehaviorCallback.java
new file mode 100644
index 0000000..14495de
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/mouse/MouseBehaviorCallback.java
@@ -0,0 +1,73 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.mouse;
+
+import javax.media.j3d.Transform3D;
+
+/**
+ * The MouseBehaviorCallback interface allows a class to be notified
+ * when the transform is changed by one of the MouseBehaviors. The
+ * class that is interested in transform changes implements this
+ * interface, and the object created with that class is registered
+ * with the desired subclass of MouseBehavior using the
+ * <code>setupCallback</code> method. When the transform changes, the
+ * registered object's <code>transformChanged</code> method is
+ * invoked.
+ */
+
+public interface MouseBehaviorCallback {
+
+ public final static int ROTATE=0;
+ public final static int TRANSLATE=1;
+ public final static int ZOOM=2;
+
+ /**
+ * Classes implementing this interface that are registered with
+ * one of the MouseBehaviors will be called every time the
+ * behavior updates the Transform
+ * @param type will be one of ROTATE, TRANSLATE or ZOOM
+ */
+ public void transformChanged( int type, Transform3D transform );
+}
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/mouse/MouseRotate.java b/src/classes/share/com/sun/j3d/utils/behaviors/mouse/MouseRotate.java
new file mode 100644
index 0000000..07309de
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/mouse/MouseRotate.java
@@ -0,0 +1,315 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.mouse;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+/**
+ * MouseRotate is a Java3D behavior object that lets users control the
+ * rotation of an object via a mouse.
+ * <p>
+ * To use this utility, first create a transform group that this
+ * rotate behavior will operate on. Then,
+ *<blockquote><pre>
+ *
+ * MouseRotate behavior = new MouseRotate();
+ * behavior.setTransformGroup(objTrans);
+ * objTrans.addChild(behavior);
+ * behavior.setSchedulingBounds(bounds);
+ *
+ *</pre></blockquote>
+ * The above code will add the rotate behavior to the transform
+ * group. The user can rotate any object attached to the objTrans.
+ */
+
+public class MouseRotate extends MouseBehavior {
+ double x_angle, y_angle;
+ double x_factor = .03;
+ double y_factor = .03;
+
+ private MouseBehaviorCallback callback = null;
+
+ /**
+ * Creates a rotate behavior given the transform group.
+ * @param transformGroup The transformGroup to operate on.
+ */
+ public MouseRotate(TransformGroup transformGroup) {
+ super(transformGroup);
+ }
+
+ /**
+ * Creates a default mouse rotate behavior.
+ **/
+ public MouseRotate() {
+ super(0);
+ }
+
+ /**
+ * Creates a rotate behavior.
+ * Note that this behavior still needs a transform
+ * group to work on (use setTransformGroup(tg)) and
+ * the transform group must add this behavior.
+ * @param flags interesting flags (wakeup conditions).
+ */
+ public MouseRotate(int flags) {
+ super(flags);
+ }
+
+ /**
+ * Creates a rotate behavior that uses AWT listeners and behavior
+ * posts rather than WakeupOnAWTEvent. The behavior is added to the
+ * specified Component. A null component can be passed to specify
+ * the behavior should use listeners. Components can then be added
+ * to the behavior with the addListener(Component c) method.
+ * @param c The Component to add the MouseListener
+ * and MouseMotionListener to.
+ * @since Java 3D 1.2.1
+ */
+ public MouseRotate(Component c) {
+ super(c, 0);
+ }
+
+ /**
+ * Creates a rotate behavior that uses AWT listeners and behavior
+ * posts rather than WakeupOnAWTEvent. The behaviors is added to
+ * the specified Component and works on the given TransformGroup.
+ * A null component can be passed to specify the behavior should use
+ * listeners. Components can then be added to the behavior with the
+ * addListener(Component c) method.
+ * @param c The Component to add the MouseListener and
+ * MouseMotionListener to.
+ * @param transformGroup The TransformGroup to operate on.
+ * @since Java 3D 1.2.1
+ */
+ public MouseRotate(Component c, TransformGroup transformGroup) {
+ super(c, transformGroup);
+ }
+
+ /**
+ * Creates a rotate behavior that uses AWT listeners and behavior
+ * posts rather than WakeupOnAWTEvent. The behavior is added to the
+ * specified Component. A null component can be passed to specify
+ * the behavior should use listeners. Components can then be added to
+ * the behavior with the addListener(Component c) method.
+ * Note that this behavior still needs a transform
+ * group to work on (use setTransformGroup(tg)) and the transform
+ * group must add this behavior.
+ * @param flags interesting flags (wakeup conditions).
+ * @since Java 3D 1.2.1
+ */
+ public MouseRotate(Component c, int flags) {
+ super(c, flags);
+ }
+
+ public void initialize() {
+ super.initialize();
+ x_angle = 0;
+ y_angle = 0;
+ if ((flags & INVERT_INPUT) == INVERT_INPUT) {
+ invert = true;
+ x_factor *= -1;
+ y_factor *= -1;
+ }
+ }
+
+ /**
+ * Return the x-axis movement multipler.
+ **/
+ public double getXFactor() {
+ return x_factor;
+ }
+
+ /**
+ * Return the y-axis movement multipler.
+ **/
+ public double getYFactor() {
+ return y_factor;
+ }
+
+
+ /**
+ * Set the x-axis amd y-axis movement multipler with factor.
+ **/
+ public void setFactor( double factor) {
+ x_factor = y_factor = factor;
+ }
+
+ /**
+ * Set the x-axis amd y-axis movement multipler with xFactor and yFactor
+ * respectively.
+ **/
+ public void setFactor( double xFactor, double yFactor) {
+ x_factor = xFactor;
+ y_factor = yFactor;
+ }
+
+ public void processStimulus (Enumeration criteria) {
+ WakeupCriterion wakeup;
+ AWTEvent[] events;
+ MouseEvent evt;
+// int id;
+// int dx, dy;
+
+ while (criteria.hasMoreElements()) {
+ wakeup = (WakeupCriterion) criteria.nextElement();
+ if (wakeup instanceof WakeupOnAWTEvent) {
+ events = ((WakeupOnAWTEvent)wakeup).getAWTEvent();
+ if (events.length > 0) {
+ evt = (MouseEvent) events[events.length-1];
+ doProcess(evt);
+ }
+ }
+
+ else if (wakeup instanceof WakeupOnBehaviorPost) {
+ while (true) {
+ // access to the queue must be synchronized
+ synchronized (mouseq) {
+ if (mouseq.isEmpty()) break;
+ evt = (MouseEvent)mouseq.remove(0);
+ // consolidate MOUSE_DRAG events
+ while ((evt.getID() == MouseEvent.MOUSE_DRAGGED) &&
+ !mouseq.isEmpty() &&
+ (((MouseEvent)mouseq.get(0)).getID() ==
+ MouseEvent.MOUSE_DRAGGED)) {
+ evt = (MouseEvent)mouseq.remove(0);
+ }
+ }
+ doProcess(evt);
+ }
+ }
+
+ }
+ wakeupOn (mouseCriterion);
+ }
+
+ void doProcess(MouseEvent evt) {
+ int id;
+ int dx, dy;
+
+ processMouseEvent(evt);
+ if (((buttonPress)&&((flags & MANUAL_WAKEUP) == 0)) ||
+ ((wakeUp)&&((flags & MANUAL_WAKEUP) != 0))) {
+ id = evt.getID();
+ if ((id == MouseEvent.MOUSE_DRAGGED) &&
+ !evt.isMetaDown() && ! evt.isAltDown()){
+ x = evt.getX();
+ y = evt.getY();
+
+ dx = x - x_last;
+ dy = y - y_last;
+
+ if (!reset){
+ x_angle = dy * y_factor;
+ y_angle = dx * x_factor;
+
+ transformX.rotX(x_angle);
+ transformY.rotY(y_angle);
+
+ transformGroup.getTransform(currXform);
+
+ Matrix4d mat = new Matrix4d();
+ // Remember old matrix
+ currXform.get(mat);
+
+ // Translate to origin
+ currXform.setTranslation(new Vector3d(0.0,0.0,0.0));
+ if (invert) {
+ currXform.mul(currXform, transformX);
+ currXform.mul(currXform, transformY);
+ } else {
+ currXform.mul(transformX, currXform);
+ currXform.mul(transformY, currXform);
+ }
+
+ // Set old translation back
+ Vector3d translation = new
+ Vector3d(mat.m03, mat.m13, mat.m23);
+ currXform.setTranslation(translation);
+
+ // Update xform
+ transformGroup.setTransform(currXform);
+
+ transformChanged( currXform );
+
+ if (callback!=null)
+ callback.transformChanged( MouseBehaviorCallback.ROTATE,
+ currXform );
+ }
+ else {
+ reset = false;
+ }
+
+ x_last = x;
+ y_last = y;
+ }
+ else if (id == MouseEvent.MOUSE_PRESSED) {
+ x_last = evt.getX();
+ y_last = evt.getY();
+ }
+ }
+ }
+
+ /**
+ * Users can overload this method which is called every time
+ * the Behavior updates the transform
+ *
+ * Default implementation does nothing
+ */
+ public void transformChanged( Transform3D transform ) {
+ }
+
+
+ /**
+ * The transformChanged method in the callback class will
+ * be called every time the transform is updated
+ */
+ public void setupCallback( MouseBehaviorCallback callback ) {
+ this.callback = callback;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/mouse/MouseTranslate.java b/src/classes/share/com/sun/j3d/utils/behaviors/mouse/MouseTranslate.java
new file mode 100644
index 0000000..896359a
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/mouse/MouseTranslate.java
@@ -0,0 +1,290 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.mouse;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+/**
+ * MouseTranslate is a Java3D behavior object that lets users control the
+ * translation (X, Y) of an object via a mouse drag motion with the third
+ * mouse button (alt-click on PC). See MouseRotate for similar usage info.
+ */
+
+public class MouseTranslate extends MouseBehavior {
+
+ double x_factor = .02;
+ double y_factor = .02;
+ Vector3d translation = new Vector3d();
+
+ private MouseBehaviorCallback callback = null;
+
+ /**
+ * Creates a mouse translate behavior given the transform group.
+ * @param transformGroup The transformGroup to operate on.
+ */
+ public MouseTranslate(TransformGroup transformGroup) {
+ super(transformGroup);
+ }
+
+ /**
+ * Creates a default translate behavior.
+ */
+ public MouseTranslate(){
+ super(0);
+ }
+
+ /**
+ * Creates a translate behavior.
+ * Note that this behavior still needs a transform
+ * group to work on (use setTransformGroup(tg)) and
+ * the transform group must add this behavior.
+ * @param flags
+ */
+ public MouseTranslate(int flags) {
+ super(flags);
+ }
+
+ /**
+ * Creates a translate behavior that uses AWT listeners and behavior
+ * posts rather than WakeupOnAWTEvent. The behavior is added to the
+ * specified Component. A null component can be passed to specify
+ * the behavior should use listeners. Components can then be added
+ * to the behavior with the addListener(Component c) method.
+ * @param c The Component to add the MouseListener
+ * and MouseMotionListener to.
+ * @since Java 3D 1.2.1
+ */
+ public MouseTranslate(Component c) {
+ super(c, 0);
+ }
+
+ /**
+ * Creates a translate behavior that uses AWT listeners and behavior
+ * posts rather than WakeupOnAWTEvent. The behaviors is added to
+ * the specified Component and works on the given TransformGroup.
+ * A null component can be passed to specify the behavior should use
+ * listeners. Components can then be added to the behavior with the
+ * addListener(Component c) method.
+ * @param c The Component to add the MouseListener and
+ * MouseMotionListener to.
+ * @param transformGroup The TransformGroup to operate on.
+ * @since Java 3D 1.2.1
+ */
+ public MouseTranslate(Component c, TransformGroup transformGroup) {
+ super(c, transformGroup);
+ }
+
+ /**
+ * Creates a translate behavior that uses AWT listeners and behavior
+ * posts rather than WakeupOnAWTEvent. The behavior is added to the
+ * specified Component. A null component can be passed to specify
+ * the behavior should use listeners. Components can then be added to
+ * the behavior with the addListener(Component c) method.
+ * Note that this behavior still needs a transform
+ * group to work on (use setTransformGroup(tg)) and the transform
+ * group must add this behavior.
+ * @param flags interesting flags (wakeup conditions).
+ * @since Java 3D 1.2.1
+ */
+ public MouseTranslate(Component c, int flags) {
+ super(c, flags);
+ }
+
+ public void initialize() {
+ super.initialize();
+ if ((flags & INVERT_INPUT) == INVERT_INPUT) {
+ invert = true;
+ x_factor *= -1;
+ y_factor *= -1;
+ }
+ }
+
+ /**
+ * Return the x-axis movement multipler.
+ **/
+ public double getXFactor() {
+ return x_factor;
+ }
+
+ /**
+ * Return the y-axis movement multipler.
+ **/
+ public double getYFactor() {
+ return y_factor;
+ }
+
+ /**
+ * Set the x-axis amd y-axis movement multipler with factor.
+ **/
+ public void setFactor( double factor) {
+ x_factor = y_factor = factor;
+ }
+
+ /**
+ * Set the x-axis amd y-axis movement multipler with xFactor and yFactor
+ * respectively.
+ **/
+ public void setFactor( double xFactor, double yFactor) {
+ x_factor = xFactor;
+ y_factor = yFactor;
+ }
+
+ public void processStimulus (Enumeration criteria) {
+ WakeupCriterion wakeup;
+ AWTEvent[] events;
+ MouseEvent evt;
+// int id;
+// int dx, dy;
+
+ while (criteria.hasMoreElements()) {
+ wakeup = (WakeupCriterion) criteria.nextElement();
+
+ if (wakeup instanceof WakeupOnAWTEvent) {
+ events = ((WakeupOnAWTEvent)wakeup).getAWTEvent();
+ if (events.length > 0) {
+ evt = (MouseEvent) events[events.length-1];
+ doProcess(evt);
+ }
+ }
+
+ else if (wakeup instanceof WakeupOnBehaviorPost) {
+ while (true) {
+ // access to the queue must be synchronized
+ synchronized (mouseq) {
+ if (mouseq.isEmpty()) break;
+ evt = (MouseEvent)mouseq.remove(0);
+ // consolodate MOUSE_DRAG events
+ while ((evt.getID() == MouseEvent.MOUSE_DRAGGED) &&
+ !mouseq.isEmpty() &&
+ (((MouseEvent)mouseq.get(0)).getID() ==
+ MouseEvent.MOUSE_DRAGGED)) {
+ evt = (MouseEvent)mouseq.remove(0);
+ }
+ }
+ doProcess(evt);
+ }
+ }
+
+ }
+ wakeupOn(mouseCriterion);
+ }
+
+ void doProcess(MouseEvent evt) {
+ int id;
+ int dx, dy;
+
+ processMouseEvent(evt);
+
+ if (((buttonPress)&&((flags & MANUAL_WAKEUP) == 0)) ||
+ ((wakeUp)&&((flags & MANUAL_WAKEUP) != 0))){
+ id = evt.getID();
+ if ((id == MouseEvent.MOUSE_DRAGGED) &&
+ !evt.isAltDown() && evt.isMetaDown()) {
+
+ x = evt.getX();
+ y = evt.getY();
+
+ dx = x - x_last;
+ dy = y - y_last;
+
+ if ((!reset) && ((Math.abs(dy) < 50) && (Math.abs(dx) < 50))) {
+ //System.out.println("dx " + dx + " dy " + dy);
+ transformGroup.getTransform(currXform);
+
+ translation.x = dx*x_factor;
+ translation.y = -dy*y_factor;
+
+ transformX.set(translation);
+
+ if (invert) {
+ currXform.mul(currXform, transformX);
+ } else {
+ currXform.mul(transformX, currXform);
+ }
+
+ transformGroup.setTransform(currXform);
+
+ transformChanged( currXform );
+
+ if (callback!=null)
+ callback.transformChanged( MouseBehaviorCallback.TRANSLATE,
+ currXform );
+
+ }
+ else {
+ reset = false;
+ }
+ x_last = x;
+ y_last = y;
+ }
+ else if (id == MouseEvent.MOUSE_PRESSED) {
+ x_last = evt.getX();
+ y_last = evt.getY();
+ }
+ }
+ }
+
+ /**
+ * Users can overload this method which is called every time
+ * the Behavior updates the transform
+ *
+ * Default implementation does nothing
+ */
+ public void transformChanged( Transform3D transform ) {
+ }
+
+ /**
+ * The transformChanged method in the callback class will
+ * be called every time the transform is updated
+ */
+ public void setupCallback( MouseBehaviorCallback callback ) {
+ this.callback = callback;
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/mouse/MouseZoom.java b/src/classes/share/com/sun/j3d/utils/behaviors/mouse/MouseZoom.java
new file mode 100644
index 0000000..1888ba1
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/mouse/MouseZoom.java
@@ -0,0 +1,271 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.mouse;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+
+/**
+ * MouseZoom is a Java3D behavior object that lets users control the
+ * Z axis translation of an object via a mouse drag motion with the second
+ * mouse button. See MouseRotate for similar usage info.
+ */
+
+public class MouseZoom extends MouseBehavior {
+
+ double z_factor = .04;
+ Vector3d translation = new Vector3d();
+
+ private MouseBehaviorCallback callback = null;
+
+ /**
+ * Creates a zoom behavior given the transform group.
+ * @param transformGroup The transformGroup to operate on.
+ */
+ public MouseZoom(TransformGroup transformGroup) {
+ super(transformGroup);
+ }
+
+ /**
+ * Creates a default mouse zoom behavior.
+ **/
+ public MouseZoom(){
+ super(0);
+ }
+
+ /**
+ * Creates a zoom behavior.
+ * Note that this behavior still needs a transform
+ * group to work on (use setTransformGroup(tg)) and
+ * the transform group must add this behavior.
+ * @param flags
+ */
+ public MouseZoom(int flags) {
+ super(flags);
+ }
+
+ /**
+ * Creates a zoom behavior that uses AWT listeners and behavior
+ * posts rather than WakeupOnAWTEvent. The behavior is added to the
+ * specified Component. A null component can be passed to specify
+ * the behavior should use listeners. Components can then be added
+ * to the behavior with the addListener(Component c) method.
+ * @param c The Component to add the MouseListener
+ * and MouseMotionListener to.
+ * @since Java 3D 1.2.1
+ */
+ public MouseZoom(Component c) {
+ super(c, 0);
+ }
+
+ /**
+ * Creates a zoom behavior that uses AWT listeners and behavior
+ * posts rather than WakeupOnAWTEvent. The behaviors is added to
+ * the specified Component and works on the given TransformGroup.
+ * @param c The Component to add the MouseListener and
+ * MouseMotionListener to. A null component can be passed to specify
+ * the behavior should use listeners. Components can then be added
+ * to the behavior with the addListener(Component c) method.
+ * @param transformGroup The TransformGroup to operate on.
+ * @since Java 3D 1.2.1
+ */
+ public MouseZoom(Component c, TransformGroup transformGroup) {
+ super(c, transformGroup);
+ }
+
+ /**
+ * Creates a zoom behavior that uses AWT listeners and behavior
+ * posts rather than WakeupOnAWTEvent. The behavior is added to the
+ * specified Component. A null component can be passed to specify
+ * the behavior should use listeners. Components can then be added
+ * to the behavior with the addListener(Component c) method.
+ * Note that this behavior still needs a transform
+ * group to work on (use setTransformGroup(tg)) and the transform
+ * group must add this behavior.
+ * @param flags interesting flags (wakeup conditions).
+ * @since Java 3D 1.2.1
+ */
+ public MouseZoom(Component c, int flags) {
+ super(c, flags);
+ }
+
+ public void initialize() {
+ super.initialize();
+ if ((flags & INVERT_INPUT) == INVERT_INPUT) {
+ z_factor *= -1;
+ invert = true;
+ }
+ }
+
+ /**
+ * Return the y-axis movement multipler.
+ **/
+ public double getFactor() {
+ return z_factor;
+ }
+
+ /**
+ * Set the y-axis movement multipler with factor.
+ **/
+ public void setFactor( double factor) {
+ z_factor = factor;
+ }
+
+
+ public void processStimulus (Enumeration criteria) {
+ WakeupCriterion wakeup;
+ AWTEvent[] events;
+ MouseEvent evt;
+// int id;
+// int dx, dy;
+
+ while (criteria.hasMoreElements()) {
+ wakeup = (WakeupCriterion) criteria.nextElement();
+ if (wakeup instanceof WakeupOnAWTEvent) {
+ events = ((WakeupOnAWTEvent)wakeup).getAWTEvent();
+ if (events.length > 0) {
+ evt = (MouseEvent) events[events.length-1];
+ doProcess(evt);
+ }
+ }
+
+ else if (wakeup instanceof WakeupOnBehaviorPost) {
+ while (true) {
+ synchronized (mouseq) {
+ if (mouseq.isEmpty()) break;
+ evt = (MouseEvent)mouseq.remove(0);
+ // consolodate MOUSE_DRAG events
+ while((evt.getID() == MouseEvent.MOUSE_DRAGGED) &&
+ !mouseq.isEmpty() &&
+ (((MouseEvent)mouseq.get(0)).getID() ==
+ MouseEvent.MOUSE_DRAGGED)) {
+ evt = (MouseEvent)mouseq.remove(0);
+ }
+ }
+ doProcess(evt);
+ }
+ }
+
+ }
+ wakeupOn (mouseCriterion);
+ }
+
+ void doProcess(MouseEvent evt) {
+ int id;
+ int dx, dy;
+
+ processMouseEvent(evt);
+
+ if (((buttonPress)&&((flags & MANUAL_WAKEUP) == 0)) ||
+ ((wakeUp)&&((flags & MANUAL_WAKEUP) != 0))){
+ id = evt.getID();
+ if ((id == MouseEvent.MOUSE_DRAGGED) &&
+ evt.isAltDown() && !evt.isMetaDown()){
+
+ x = evt.getX();
+ y = evt.getY();
+
+ dx = x - x_last;
+ dy = y - y_last;
+
+ if (!reset){
+ transformGroup.getTransform(currXform);
+
+ translation.z = dy*z_factor;
+
+ transformX.set(translation);
+
+ if (invert) {
+ currXform.mul(currXform, transformX);
+ } else {
+ currXform.mul(transformX, currXform);
+ }
+
+ transformGroup.setTransform(currXform);
+
+ transformChanged( currXform );
+
+ if (callback!=null)
+ callback.transformChanged( MouseBehaviorCallback.ZOOM,
+ currXform );
+
+ }
+ else {
+ reset = false;
+ }
+
+ x_last = x;
+ y_last = y;
+ }
+ else if (id == MouseEvent.MOUSE_PRESSED) {
+ x_last = evt.getX();
+ y_last = evt.getY();
+ }
+ }
+ }
+
+
+ /**
+ * Users can overload this method which is called every time
+ * the Behavior updates the transform
+ *
+ * Default implementation does nothing
+ */
+ public void transformChanged( Transform3D transform ) {
+ }
+
+ /**
+ * The transformChanged method in the callback class will
+ * be called every time the transform is updated
+ */
+ public void setupCallback( MouseBehaviorCallback callback ) {
+ this.callback = callback;
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/picking/Intersect.java b/src/classes/share/com/sun/j3d/utils/behaviors/picking/Intersect.java
new file mode 100644
index 0000000..b53e18e
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/picking/Intersect.java
@@ -0,0 +1,1297 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.picking;
+
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import java.lang.Math;
+import com.sun.j3d.internal.J3dUtilsI18N;
+
+/*
+ * Contains static methods to aid in the intersection test between
+ * various PickShape classes and geometry primitives (such as quad,
+ * triangle, line and point).
+ */
+
+
+/**
+ * @deprecated As of Java 3D version 1.2, this class is no
+ * longer needed
+ */
+
+public class Intersect
+{
+
+ /**
+ * Determines if the <code>PickRay</code> and quadrilateral
+ * objects intersect.
+ * The quadrilateral is defined as <code>coordinates[index]</code> to
+ * <code>coordinates[index+3]</code>.
+ *
+ * @param ray The ray to use in the intersection test.
+ * @param coordinates An array holding the quadrilateral data.
+ * @param index An array index that designates the starting position
+ * in the array of the quadrilateral to test.
+ * @param dist On return dist[0] will be set to the distance between ray's
+ * origin and the point of intersection, if it exists.
+ * The dist array should be allocated by the user.
+ * @return <code>true</code> if the ray intersects the quad,
+ * <code>false</code> if the ray does not intersect the object.
+ */
+ public static boolean rayAndQuad( PickRay ray, Point3d coordinates[],
+ int index, double dist[] ) {
+
+ if((coordinates.length - index) < 4)
+ throw new RuntimeException(J3dUtilsI18N.getString("Intersect0"));
+
+ Point3d pnts[] = new Point3d[4];
+
+ for(int i=0; i<4; i++)
+ pnts[i] = coordinates[index+i];
+
+ return rayAndPoly(pnts, ray, dist);
+
+ }
+
+ /**
+ * Return true if triangle intersects with ray and the distance, from
+ * the origin of ray to the intersection point, is stored in dist[0].
+ * The triangle is defined by coordinates[index] to coordinates[index+2]
+ * <code>coordinates[index+2]</code>.
+ *
+ * @param ray The ray to use in the intersection test.
+ * @param coordinates An array holding the triangle data.
+ * @param index An array index that designates the starting position
+ * in the array of the triangle to test.
+ * @param dist On return dist[0] will be set to the distance between ray's origin and the
+ * point of intersection, if it exists. The dist array should be
+ * allocated by the user.
+ * @return <code>true</code> if the ray intersects the triangle,
+ * <code>false</code> if the ray does not intersect the object.
+ */
+ public static boolean rayAndTriangle( PickRay ray, Point3d coordinates[],
+ int index, double dist[] ) {
+
+ if((coordinates.length - index) < 3)
+ throw new RuntimeException(J3dUtilsI18N.getString("Intersect1"));
+
+ Point3d pnts[] = new Point3d[3];
+
+ for(int i=0; i<3; i++)
+ pnts[i] = coordinates[index+i];
+
+ return rayAndPoly(pnts, ray, dist);
+
+ }
+
+ /**
+ * Return true if triangle intersects with ray and the distance, from
+ * the origin of ray to the intersection point, is stored in dist[0].
+ * The triangle is defined by coordinates[index] to coordinates[index+2]
+ *
+ * @param ray The ray that is used in intersection test.
+ * @param coordinates an array of vertices.
+ * @param index the vertex index
+ * @param dist On return dist[0] will be set to the distance between ray's origin and the point intersection, if
+ * exist.
+ * @return true if ray intersects triangle, else return false.
+ */
+
+ public static boolean rayAndTriangle( PickRay ray, Point3f coordinates[],
+ int index, double dist[] ) {
+
+ if((coordinates.length - index) < 3)
+ throw new RuntimeException(J3dUtilsI18N.getString("Intersect1"));
+
+ Point3d pnts[] = new Point3d[3];
+
+ for(int i=0; i<3; i++)
+ pnts[i] = new Point3d(coordinates[index+i]);
+
+ return rayAndPoly(pnts, ray, dist);
+
+ }
+
+
+ /**
+ * Caluates the intersection between a <code>PickSegment</code>
+ * object and a quadrilateral.
+ * The quad is defined as coordinates[index] to coordinates[index+3]
+ *
+ * @param segment The segment to use in the intersection test.
+ * @param coordinates An array holding the quadrilateral data.
+ * @param index An array index that designates the starting position
+ * in the array of the quadrilateral to test.
+ * @param dist On return dist[0] will be set to the distance between the start of the segment
+ * and the point of intersection, if it exists. The dist array
+ * should be allocated by the user.
+ * @return <code>true</code> if the segment intersects the quad,
+ * <code>false</code> if the segment does not intersect the object.
+ */
+ public static boolean segmentAndQuad( PickSegment segment,
+ Point3d coordinates[],
+ int index, double dist[] ) {
+ if((coordinates.length - index) < 4)
+ throw new RuntimeException(J3dUtilsI18N.getString("Intersect3"));
+
+ Point3d pnts[] = new Point3d[4];
+
+ for(int i=0; i<4; i++)
+ pnts[i] = coordinates[index+i];
+
+ return segmentAndPoly(pnts, segment, dist);
+
+ }
+
+ /**
+ * Return true if quad intersects with segment and the distance, from
+ * the start of segment to the intersection point, is stored in dist[0].
+ * The quad is defined by coordinates[index] to coordinates[index+3]
+ *
+ * @param segment The segment that is used in intersection test.
+ * @param coordinates an array of vertices.
+ * @param index the vertex index
+ * @param dist On return dist[0] will be set to the distance between segment's start and the point
+ * intersection, if exist.
+ * @return true if segment intersects quad, else return false.
+ */
+
+ public static boolean segmentAndQuad( PickSegment segment, Point3f coordinates[],
+ int index, double dist[] ) {
+ if((coordinates.length - index) < 4)
+ throw new RuntimeException(J3dUtilsI18N.getString("Intersect3"));
+
+ Point3d pnts[] = new Point3d[4];
+
+ for(int i=0; i<4; i++)
+ pnts[i] = new Point3d(coordinates[index+i]);
+
+ return segmentAndPoly(pnts, segment, dist);
+
+ }
+
+ /**
+ * Caluates the intersection between a <code>PickSegment</code>
+ * object and a triangle.
+ * The triangle is defined as coordinates[index] to coordinates[index+2]
+ *
+ * @param segment The segment to use in the intersection test.
+ * @param coordinates An array holding the triangle data.
+ * @param index An array index that designates the starting position
+ * in the array of the triangle to test.
+ * @param dist On return dist[0] contains the distance between the start of the segment
+ * and the point of intersection, if it exists. The dist array
+ * should be allocated by the user.
+ * @return <code>true</code> if the segment intersects the triangle,
+ * <code>false</code> if the segment does not intersect the object.
+ */
+ public static boolean segmentAndTriangle( PickSegment segment,
+ Point3d coordinates[],
+ int index,
+ double dist[] ) {
+ if((coordinates.length - index) < 3)
+ throw new RuntimeException(J3dUtilsI18N.getString("Intersect5"));
+
+ Point3d pnts[] = new Point3d[3];
+
+ for(int i=0; i<3; i++)
+ pnts[i] = coordinates[index+i];
+
+ return segmentAndPoly(pnts, segment, dist);
+
+ }
+
+ /**
+ * Return true if triangle intersects with segment and the distance, from
+ * the start of segment to the intersection point, is stored in dist[0].
+ * The triangle is defined by coordinates[index] to coordinates[index+2]
+ *
+ * @param segment The segment that is used in intersection test.
+ * @param coordinates an array of vertices.
+ * @param index the vertex index
+ * @param dist On return dist[0] will be set to the distance between segment's start and the point
+ * intersection, if exist.
+ * @return true if segment intersects triangle, else return false.
+ */
+
+ public static boolean segmentAndTriangle( PickSegment segment,
+ Point3f coordinates[], int index,
+ double dist[] ) {
+ if((coordinates.length - index) < 3)
+ throw new RuntimeException(J3dUtilsI18N.getString("Intersect6"));
+
+ Point3d pnts[] = new Point3d[3];
+
+ for(int i=0; i<3; i++)
+ pnts[i] = new Point3d(coordinates[index+i]);
+
+ return segmentAndPoly(pnts, segment, dist);
+
+ }
+
+ /**
+ * Caluates the intersection between a <code>PickPoint</code>
+ * object and a quadrilateral.
+ * The quad is defined as <code>coordinates[index]</code> to
+ * <code>coordinates[index+3]</code>.
+ *
+ * @param point The point to use in the intersection test.
+ * @param coordinates An array holding the quadrilateral data.
+ * @param index An array index that designates the starting position
+ * in the array of the quadrilateral to test.
+ * @return <code>true</code> if the point intersects the quad,
+ * <code>false</code> if the point does not intersect the object.
+ */
+ private static boolean pointAndQuad( PickPoint point,
+ Point3d coordinates[],
+ int index) {
+
+ if((coordinates.length - index) < 4)
+ throw new RuntimeException(J3dUtilsI18N.getString("Intersect7"));
+
+ Point3d pnts[] = new Point3d[4];
+
+ for(int i=0; i<4; i++)
+ pnts[i] = coordinates[index+i];
+
+ return pointAndPoly( pnts, point);
+
+ }
+
+ /**
+ * Return true if quad intersects with point.
+ * The triangle is defined by coordinates[index] to coordinates[index+3]
+ *
+ * @param point The point that is used in intersection test.
+ * @param coordinates an array of vertices.
+ * @param index the vertex index
+ * @return true if point intersects quad, else return false.
+ */
+
+ private static boolean pointAndQuad( PickPoint point, Point3f coordinates[],
+ int index) {
+
+ if((coordinates.length - index) < 4)
+ throw new RuntimeException(J3dUtilsI18N.getString("Intersect7"));
+
+ Point3d pnts[] = new Point3d[4];
+
+ for(int i=0; i<4; i++)
+ pnts[i] = new Point3d(coordinates[index+i]);
+
+ return pointAndPoly( pnts, point);
+
+ }
+
+ /**
+ * Caluates the intersection between a <code>PickPoint</code>
+ * object and a triangle.
+ * The triangle is defined by <code>coordinates[index]</code> to
+ * <code>coordinates[index+2]</code>.
+ *
+ * @param point The point to use in the intersection test.
+ * @param coordinates An array holding the triangle data.
+ * @param index An array index that designates the starting position
+ * in the array of the triangle to test.
+ * @return <code>true</code> if the point intersects the triangle,
+ * <code>false</code> if the point does not intersect the object.
+ */
+ private static boolean pointAndTriangle( PickPoint point,
+ Point3d coordinates[],
+ int index) {
+
+ if((coordinates.length - index) < 3)
+ throw new RuntimeException(J3dUtilsI18N.getString("Intersect9"));
+
+ Point3d pnts[] = new Point3d[3];
+
+ for(int i=0; i<3; i++)
+ pnts[i] = coordinates[index+i];
+
+ return pointAndPoly( pnts, point);
+
+ }
+
+ /**
+ * Return true if triangle intersects with point.
+ * The triangle is defined by coordinates[index] to coordinates[index+2]
+ *
+ * @param point The point that is used in intersection test.
+ * @param coordinates an array of vertices.
+ * @param index the vertex index
+ * @return true if point intersects triangle, else return false.
+ */
+
+ private static boolean pointAndTriangle( PickPoint point, Point3f coordinates[],
+ int index) {
+
+ if((coordinates.length - index) < 3)
+ throw new RuntimeException(J3dUtilsI18N.getString("Intersect10"));
+
+ Point3d pnts[] = new Point3d[3];
+
+ for(int i=0; i<3; i++)
+ pnts[i] = new Point3d(coordinates[index+i]);
+
+ return pointAndPoly( pnts, point);
+
+ }
+
+ /**
+ * Determines if the <code>PickRay</code> and <code>Point3d</code>
+ * objects intersect.
+ *
+ * @param ray The ray that is used in the intersection test.
+ * @param pnt The point that is used in intersection test.
+ * @param dist On return dist[0] will be set to the distance between ray's origin and the point
+ * of intersection, if it exists. The dist array
+ * should be allocated by the user.
+ * @return <code>true</code> if the ray intersects the point,
+ * <code>false</code> if the ray does not intersect the object.
+ */
+ public static boolean rayAndPoint( PickRay ray, Point3d pnt,
+ double dist[] ) {
+
+ Point3d origin = new Point3d();
+ Vector3d direction = new Vector3d();
+
+ ray.get(origin, direction);
+
+ return rayAndPoint(pnt, origin, direction, dist);
+ }
+
+ /**
+ * Return true if point intersects with ray and the distance, from
+ * the origin of ray to the intersection point, is stored in dist[0].
+ *
+ * @param ray The ray that is used in intersection test.
+ * @param pnt The point that is used in intersection test.
+ * @param dist On return dist[0] contains the distance between ray's origin and the point
+ * intersection, if exist.
+ * @return true if ray intersects point, else return false.
+ */
+
+ public static boolean rayAndPoint( PickRay ray, Point3f pnt, double dist[] ) {
+
+ Point3d origin = new Point3d();
+ Vector3d direction = new Vector3d();
+
+ ray.get(origin, direction);
+
+ return rayAndPoint(new Point3d(pnt), origin, direction, dist);
+ }
+
+ /**
+ * Determines if the <code>PickSegment</code> and <code>Point3d</code>
+ * objects intersect.
+ *
+ * @param segment The segment that is used in the intersection test.
+ * @param pnt The point that is used in intersection test.
+ * @param dist On return dist[0] contains the distance between segment's origin and the point
+ * of intersection, if it exists. The dist array
+ * should be allocated by the user.
+ * @return <code>true</code> if the segment intersects the point,
+ * <code>false</code> if the segment does not intersect the object.
+ */
+ public static boolean segmentAndPoint( PickSegment segment, Point3d pnt,
+ double dist[] ) {
+
+ Point3d start = new Point3d();
+ Point3d end = new Point3d();
+ Vector3d direction = new Vector3d();
+
+ segment.get(start, end);
+ direction.x = end.x - start.x;
+ direction.y = end.y - start.y;
+ direction.z = end.z - start.z;
+
+ if((rayAndPoint(pnt, start, direction, dist)==true) && (dist[0] <= 1.0))
+ return true;
+
+ return false;
+ }
+
+ /**
+ * Return true if point intersects with segment and the distance, from
+ * the start of segment to the intersection point, is stored in dist[0].
+ *
+ * @param segment The segment that is used in intersection test.
+ * @param pnt The point that is used in intersection test.
+ * @param dist On return dist[0] contains the distance between segment's start and the point
+ * intersection, if exist.
+ * @return true if segment intersects point, else return false.
+ */
+
+ public static boolean segmentAndPoint( PickSegment segment, Point3f pnt,
+ double dist[] ) {
+
+ Point3d start = new Point3d();
+ Point3d end = new Point3d();
+ Vector3d direction = new Vector3d();
+
+ segment.get(start, end);
+ direction.x = end.x - start.x;
+ direction.y = end.y - start.y;
+ direction.z = end.z - start.z;
+
+ if((rayAndPoint(new Point3d(pnt), start, direction, dist)==true)
+ && (dist[0] <= 1.0))
+ return true;
+
+ return false;
+ }
+
+ /**
+ * Determines if the <code>PickPoint</code> and <code>Point3d</code>
+ * objects intersect.
+ *
+ * @param point The PickPoint that is used in the intersection test.
+ * @param pnt The Point3d that is used in intersection test.
+ * @return <code>true</code> if the PickPoint and Point3d objects
+ * intersect, <code>false</code> if the do not intersect.
+ */
+ public static boolean pointAndPoint( PickPoint point, Point3d pnt) {
+
+ Point3d location = new Point3d();
+
+ point.get(location);
+
+ if ((location.x == pnt.x) && (location.y == pnt.y) &&
+ (location.z == pnt.z))
+ return true;
+
+ return false;
+ }
+
+ /**
+ * Return true if pnt intersects with point.
+ *
+ * @param point The point that is used in intersection test.
+ * @param pnt The point that is used in intersection test.
+ * @return true if point intersects pnt, else return false.
+ */
+
+ public static boolean pointAndPoint( PickPoint point, Point3f pnt) {
+
+ Point3d location = new Point3d();
+
+ point.get(location);
+
+ if(((float) location.x == pnt.x) && ((float) location.y == pnt.y)
+ && ((float) location.z == pnt.z))
+ return true;
+
+ return false;
+ }
+
+ /**
+ * Determines if the <code>PickRay</code> and Line
+ * objects intersect.
+ * The line is defined as <code>coordinates[index]</code> to
+ * <code>coordinates[index+1]</code>.
+ *
+ * @param ray The ray that is used in the intersection test.
+ * @param coordinates An array holding the line data.
+ * @param dist On return dist[0] contains the distance between ray's origin and the point of
+ * intersection, if it exists. The dist array
+ * should be allocated by the user.
+ * @return <code>true</code> if the ray intersects the line,
+ * <code>false</code> if the ray does not intersect the object.
+ */
+ public static boolean rayAndLine(PickRay ray, Point3d coordinates[],
+ int index,
+ double dist[] ) {
+ Point3d origin = new Point3d();
+ Vector3d direction = new Vector3d();
+
+ if((coordinates.length - index) < 2)
+ throw new RuntimeException(J3dUtilsI18N.getString("Intersect11"));
+
+ ray.get(origin, direction);
+ Point3d start = coordinates[index++];
+ Point3d end = coordinates[index];
+
+ return lineAndRay( start, end, origin, direction, dist );
+
+ }
+
+ /**
+ * Return true if line intersects with ray and the distance, from
+ * the origin of ray to the intersection point, is stored in dist[0].
+ * The line is defined by coordinates[index] to coordinates[index+1]
+ *
+ * @param ray The ray that is used in intersection test.
+ * @param coordinates an array of vertices.
+ * @param index the vertex index
+ * @param dist On return dist[0] contains the distance between ray's origin and the point intersection, if
+ * exist.
+ * @return true if ray intersects line, else return false.
+ */
+
+ public static boolean rayAndLine(PickRay ray, Point3f coordinates[], int index,
+ double dist[] ) {
+ Point3d origin = new Point3d();
+ Vector3d direction = new Vector3d();
+
+ if((coordinates.length - index) < 2)
+ throw new RuntimeException(J3dUtilsI18N.getString("Intersect11"));
+
+ ray.get(origin, direction);
+ Point3d start = new Point3d(coordinates[index++]);
+ Point3d end = new Point3d(coordinates[index]);
+
+ return lineAndRay( start, end, origin, direction, dist );
+
+ }
+
+ /**
+ * Determines if the <code>PickSegment</code> and Line
+ * objects intersect.
+ * The line is defined as <code>coordinates[index]</code> to
+ * <code>coordinates[index+1]</code>.
+ *
+ * @param segment The segment that is used in the intersection test.
+ * @param coordinates An array holding the line data.
+ * @param dist On return dist[0] contains the distance between segment's origin and the point of
+ * intersection, if it exists. The dist array
+ * should be allocated by the user.
+ * @return <code>true</code> if the segment intersects the line,
+ * <code>false</code> if the segment does not intersect the object.
+ */
+ public static boolean segmentAndLine(PickSegment segment,
+ Point3d coordinates[],
+ int index, double dist[] ) {
+
+ Point3d start = new Point3d();
+ Point3d end = new Point3d();
+ Vector3d direction = new Vector3d();
+
+ if((coordinates.length - index) < 2)
+ throw new RuntimeException(J3dUtilsI18N.getString("Intersect13"));
+
+ segment.get(start, end);
+ direction.x = end.x - start.x;
+ direction.y = end.y - start.y;
+ direction.z = end.z - start.z;
+
+ Point3d startpnt = coordinates[index++];
+ Point3d endpnt = coordinates[index];
+
+ if(lineAndRay(startpnt, endpnt, start, direction, dist)==true)
+ if(dist[0] <= 1.0)
+ return true;
+
+ return false;
+ }
+
+ /**
+ * Return true if line intersects with segment and the distance, from
+ * the start of segment to the intersection point, is stored in dist[0].
+ * The line is defined by coordinates[index] to coordinates[index+1]
+ *
+ * @param segment The segment that is used in intersection test.
+ * @param coordinates an array of vertices.
+ * @param index the vertex index
+ * @param dist On return dist[0] contains the distance between segment's start and the point
+ * intersection, if exist.
+ * @return true if segment intersects line, else return false.
+ */
+
+ public static boolean segmentAndLine(PickSegment segment, Point3f coordinates[],
+ int index, double dist[] ) {
+
+ Point3d start = new Point3d();
+ Point3d end = new Point3d();
+ Vector3d direction = new Vector3d();
+
+ if((coordinates.length - index) < 2)
+ throw new RuntimeException(J3dUtilsI18N.getString("Intersect13"));
+
+ segment.get(start, end);
+ direction.x = end.x - start.x;
+ direction.y = end.y - start.y;
+ direction.z = end.z - start.z;
+
+ Point3d startpnt = new Point3d(coordinates[index++]);
+ Point3d endpnt = new Point3d(coordinates[index]);
+
+ if(lineAndRay(startpnt, endpnt, start, direction, dist)==true)
+ if(dist[0] <= 1.0)
+ return true;
+
+ return false;
+ }
+
+ /**
+ * Determines if the <code>PickPoint</code> and Line
+ * objects intersect.
+ * The line is defined as <code>coordinates[index]</code> to
+ * <code>coordinates[index+1]</code>.
+ *
+ * @param point The point that is used in the intersection test.
+ * @param coordinates An array holding the line data.
+ * @return <code>true</code> if the the point intersects the line,
+ * <code>false</code> if the the point does not intersect the object.
+ */
+ public static boolean pointAndLine(PickPoint point, Point3d coordinates[],
+ int index ) {
+
+ if((coordinates.length - index) < 2)
+ throw new RuntimeException(J3dUtilsI18N.getString("Intersect13"));
+
+ double dist[] = new double[1];
+ Point3d start = coordinates[index++];
+ Point3d end = coordinates[index];
+ Point3d location = new Point3d();
+ Vector3d direction = new Vector3d();
+
+ point.get(location);
+ direction.x = end.x - start.x;
+ direction.y = end.y - start.y;
+ direction.z = end.z - start.z;
+
+ if ((rayAndPoint(location, start, direction, dist)==true) &&
+ (dist[0] <= 1.0))
+ return true;
+
+ return false;
+
+ }
+
+ /**
+ * Return true if line intersects with point.
+ * The line is defined by coordinates[index] to coordinates[index+1]
+ *
+ * @param point The point that is used in intersection test.
+ * @param coordinates an array of vertices.
+ * @param index the vertex index
+ * @return true if point intersects line, else return false.
+ */
+
+ public static boolean pointAndLine(PickPoint point, Point3f coordinates[],
+ int index ) {
+
+ if((coordinates.length - index) < 2)
+ throw new RuntimeException(J3dUtilsI18N.getString("Intersect13"));
+
+ double dist[] = new double[1];
+ Point3d start = new Point3d(coordinates[index++]);
+ Point3d end = new Point3d(coordinates[index]);
+ Point3d location = new Point3d();
+ Vector3d direction = new Vector3d();
+
+ point.get(location);
+ direction.x = end.x - start.x;
+ direction.y = end.y - start.y;
+ direction.z = end.z - start.z;
+
+ if((rayAndPoint(location, start, direction, dist)==true) && (dist[0] <= 1.0))
+ return true;
+
+ return false;
+
+ }
+
+ /**
+ * Return true if point is on the inside of halfspace test. The
+ * halfspace is
+ * partition by the plane of triangle or quad.
+ * */
+
+ private static boolean pointAndPoly( Point3d coordinates[], PickPoint point) {
+
+ Vector3d vec0 = new Vector3d(); // Edge vector from point 0 to point 1;
+ Vector3d vec1 = new Vector3d(); // Edge vector from point 0 to point 2 or 3;
+ Vector3d pNrm = new Vector3d();
+ double absNrmX, absNrmY, absNrmZ, pD = 0.0;
+ Vector3d tempV3d = new Vector3d();
+ double pNrmDotrDir = 0.0;
+
+ double tempD;
+
+ int i, j;
+
+ // Compute plane normal.
+ for(i=0; i<coordinates.length-1;) {
+ vec0.x = coordinates[i+1].x - coordinates[i].x;
+ vec0.y = coordinates[i+1].y - coordinates[i].y;
+ vec0.z = coordinates[i+1].z - coordinates[i++].z;
+ if(vec0.length() > 0.0)
+ break;
+ }
+
+ for(j=i; j<coordinates.length-1; j++) {
+ vec1.x = coordinates[j+1].x - coordinates[j].x;
+ vec1.y = coordinates[j+1].y - coordinates[j].y;
+ vec1.z = coordinates[j+1].z - coordinates[j].z;
+ if(vec1.length() > 0.0)
+ break;
+ }
+
+ if(j == (coordinates.length-1)) {
+ // System.out.println("(1) Degenerated polygon.");
+ return false; // Degenerated polygon.
+ }
+
+ /*
+ System.out.println("Ray orgin : " + ray.origin + " dir " + ray.direction);
+ System.out.println("Triangle/Quad :");
+ for(i=0; i<coordinates.length; i++)
+ System.out.println("P" + i + " " + coordinates[i]);
+ */
+
+ pNrm.cross(vec0,vec1);
+
+ if(pNrm.length() == 0.0) {
+ // System.out.println("(2) Degenerated polygon.");
+ return false; // Degenerated polygon.
+ }
+ // Compute plane D.
+ tempV3d.set((Tuple3d) coordinates[0]);
+ pD = pNrm.dot(tempV3d);
+
+ Point3d location = new Point3d();
+ point.get(location);
+ tempV3d.set((Tuple3d) location);
+
+ if((pD - pNrm.dot(tempV3d)) == 0.0 )
+ return true;
+
+ return false;
+
+ }
+
+ private static boolean lineAndRay(Point3d start, Point3d end,
+ Point3d ori, Vector3d dir,
+ double dist[] ) {
+
+ double m00, m01, m10, m11;
+ double mInv00, mInv01, mInv10, mInv11;
+ double dmt, t, s, tmp1, tmp2;
+ Vector3d lDir;
+
+ lDir = new Vector3d(end.x - start.x, end.y - start.y,
+ end.z - start.z);
+
+ m00 = lDir.x;
+ m01 = -dir.x;
+ m10 = lDir.y;
+ m11 = -dir.y;
+
+ // Get the determinant.
+ dmt = (m00 * m11) - (m10 * m01);
+
+ if(dmt==0.0) // No solution, hence no intersect.
+ return false;
+
+ // Find the inverse.
+ tmp1 = 1/dmt;
+
+ mInv00 = tmp1 * m11;
+ mInv01 = tmp1 * (-m01);
+ mInv10 = tmp1 * (-m10);
+ mInv11 = tmp1 * m00;
+
+ tmp1 = ori.x - start.x;
+ tmp2 = ori.y - start.y;
+
+ t = mInv00 * tmp1 + mInv01 * tmp2;
+ s = mInv10 * tmp1 + mInv11 * tmp2;
+
+ if(s<0.0) // Before the origin of ray.
+ return false;
+ if((t<0)||(t>1.0)) // Before or after the end points of line.
+ return false;
+
+ tmp1 = ori.z + s * dir.z;
+ tmp2 = start.z + t * lDir.z;
+
+ if((tmp1 < (tmp2 - Double.MIN_VALUE)) || (tmp1 > (tmp2 + Double.MIN_VALUE)))
+ return false;
+
+ dist[0] = s;
+ return true;
+ }
+
+ private static boolean rayAndPoint( Point3d pnt, Point3d ori,
+ Vector3d dir, double dist[] ) {
+ int flag = 0;
+ double temp;
+
+ if(dir.x != 0.0) {
+ flag = 0;
+ dist[0] = (pnt.x - ori.x)/dir.x;
+ }
+ else if(dir.y != 0.0) {
+ if(pnt.x != ori.x)
+ return false;
+ flag = 1;
+ dist[0] = (pnt.y - ori.y)/dir.y;
+ }
+ else if(dir.z != 0.0) {
+ if((pnt.x != ori.x)||(pnt.y != ori.y))
+ return false;
+ flag = 2;
+ dist[0] = (pnt.z - ori.z)/dir.z;
+
+ }
+ else
+ return false;
+
+ if(dist[0] < 0.0)
+ return false;
+
+ if(flag == 0) {
+ temp = ori.y + dist[0] * dir.y;
+ if((pnt.y < (temp - Double.MIN_VALUE)) || (pnt.y > (temp + Double.MIN_VALUE)))
+ return false;
+ }
+
+ if(flag < 2) {
+ temp = ori.z + dist[0] * dir.z;
+ if((pnt.z < (temp - Double.MIN_VALUE)) || (pnt.z > (temp + Double.MIN_VALUE)))
+ return false;
+ }
+
+ return true;
+
+ }
+
+ private static boolean rayAndPoly( Point3d coordinates[],
+ PickRay ray, double dist[] ) {
+
+ Vector3d vec0 = new Vector3d(); // Edge vector from point 0 to point 1;
+ Vector3d vec1 = new Vector3d(); // Edge vector from point 0 to point 2 or 3;
+ Vector3d pNrm = new Vector3d();
+ double absNrmX, absNrmY, absNrmZ, pD = 0.0;
+ Vector3d tempV3d = new Vector3d();
+ double pNrmDotrDir = 0.0;
+ int axis, nc, sh, nsh;
+ Point3d origin = new Point3d();
+ Vector3d direction = new Vector3d();
+
+ Point3d iPnt = new Point3d(); // Point of intersection.
+
+ double uCoor[] = new double[4]; // Only need to support up to quad.
+ double vCoor[] = new double[4];
+ double tempD;
+
+ int i, j;
+
+ // Compute plane normal.
+ for(i=0; i<coordinates.length-1;) {
+ vec0.x = coordinates[i+1].x - coordinates[i].x;
+ vec0.y = coordinates[i+1].y - coordinates[i].y;
+ vec0.z = coordinates[i+1].z - coordinates[i++].z;
+ if(vec0.length() > 0.0)
+ break;
+ }
+
+ for(j=i; j<coordinates.length-1; j++) {
+ vec1.x = coordinates[j+1].x - coordinates[j].x;
+ vec1.y = coordinates[j+1].y - coordinates[j].y;
+ vec1.z = coordinates[j+1].z - coordinates[j].z;
+ if(vec1.length() > 0.0)
+ break;
+ }
+
+ if(j == (coordinates.length-1)) {
+ // System.out.println("(1) Degenerated polygon.");
+ return false; // Degenerated polygon.
+ }
+
+ /*
+ System.out.println("Triangle/Quad :");
+ for(i=0; i<coordinates.length; i++)
+ System.out.println("P" + i + " " + coordinates[i]);
+ */
+
+ pNrm.cross(vec0,vec1);
+
+ if(pNrm.length() == 0.0) {
+ // System.out.println("(2) Degenerated polygon.");
+ return false; // Degenerated polygon.
+ }
+
+ ray.get(origin, direction);
+ // System.out.println("Ray orgin : " + origin + " dir " + direction);
+
+ // Compute plane D.
+ tempV3d.set((Tuple3d) coordinates[0]);
+ pD = pNrm.dot(tempV3d);
+
+ pNrmDotrDir = pNrm.dot(direction);
+
+ // Ray is parallel to plane.
+ if(pNrmDotrDir== 0.0) {
+ // System.out.println("Ray is parallel to plane.");
+ return false;
+ }
+
+ tempV3d.set((Tuple3d) origin);
+
+ dist[0] = (pD - pNrm.dot(tempV3d))/ pNrmDotrDir;
+
+ // Ray intersects the plane behind the ray's origin.
+ if(dist[0] < 0.0 ) {
+ // System.out.println("Ray intersects the plane behind the ray's origin.");
+ return false;
+ }
+
+ // Now, one thing for sure the ray intersects the plane.
+ // Find the intersection point.
+ iPnt.x = origin.x + direction.x * dist[0];
+ iPnt.y = origin.y + direction.y * dist[0];
+ iPnt.z = origin.z + direction.z * dist[0];
+
+ // System.out.println("dist " + dist[0] + " iPnt : " + iPnt);
+
+ // Project 3d points onto 2d plane and apply Jordan curve theorem.
+ // Note : Area of polygon is not preserve in this projection, but
+ // it doesn't matter here.
+
+ // Find the axis of projection.
+ absNrmX = Math.abs(pNrm.x);
+ absNrmY = Math.abs(pNrm.y);
+ absNrmZ = Math.abs(pNrm.z);
+
+ if(absNrmX > absNrmY)
+ axis = 0;
+ else
+ axis = 1;
+
+ if(axis == 0) {
+ if(absNrmX < absNrmZ)
+ axis = 2;
+ }
+ else if(axis == 1) {
+ if(absNrmY < absNrmZ)
+ axis = 2;
+ }
+
+ // System.out.println("Normal " + pNrm + " axis " + axis );
+
+ for(i=0; i<coordinates.length; i++) {
+ switch (axis) {
+ case 0:
+ uCoor[i] = coordinates[i].y - iPnt.y;
+ vCoor[i] = coordinates[i].z - iPnt.z;
+ break;
+
+ case 1:
+ uCoor[i] = coordinates[i].x - iPnt.x;
+ vCoor[i] = coordinates[i].z - iPnt.z;
+ break;
+
+ case 2:
+ uCoor[i] = coordinates[i].x - iPnt.x;
+ vCoor[i] = coordinates[i].y - iPnt.y;
+ break;
+ }
+
+ // System.out.println("i " + i + " u " + uCoor[i] + " v " + vCoor[i]);
+ }
+
+ // initialize number of crossing, nc.
+ nc = 0;
+
+ if(vCoor[0] < 0.0)
+ sh = -1;
+ else
+ sh = 1;
+
+ for(i=0; i<coordinates.length; i++) {
+ j= i+1;
+ if(j==coordinates.length)
+ j=0;
+
+ if(vCoor[j] < 0.0)
+ nsh = -1;
+ else
+ nsh = 1;
+
+ if(sh != nsh) {
+ if((uCoor[i] > 0.0) && (uCoor[j] > 0.0)) {
+ // This line must cross U+.
+ nc++;
+ }
+ else if((uCoor[i] > 0.0) || (uCoor[j] > 0.0)) {
+ // This line might cross U+. We need to compute intersection on U azis.
+ tempD = uCoor[i]-vCoor[i]*(uCoor[j]-uCoor[i])/(vCoor[j]-vCoor[i]);
+ if(tempD > 0)
+ // This line cross U+.
+ nc++;
+ }
+ sh = nsh;
+ } // sh != nsh
+ }
+
+ // System.out.println("nc " + nc);
+
+ if((nc%2) == 1) {
+
+ // calculate the distance
+ dist[0] *= direction.length();
+
+ // System.out.println("Ray Intersected!");
+ /*
+ System.out.println("Ray orgin : " + origin + " dir " + direction);
+ System.out.println("Triangle/Quad :");
+ for(i=0; i<coordinates.length; i++)
+ System.out.println("P" + i + " " + coordinates[i]);
+ System.out.println("dist " + dist[0] + " iPnt : " + iPnt);
+ */
+ return true;
+ }
+ else {
+ // System.out.println("Ray Not Intersected!");
+ return false;
+ }
+ }
+
+ /**
+ * Return true if triangle or quad intersects with segment and the distance is
+ * stored in dist[0].
+ * */
+
+ private static boolean segmentAndPoly( Point3d coordinates[],
+ PickSegment segment,
+ double dist[] ) {
+
+ Vector3d vec0 = new Vector3d(); // Edge vector from point 0 to point 1;
+ Vector3d vec1 = new Vector3d(); // Edge vector from point 0 to point 2 or 3;
+ Vector3d pNrm = new Vector3d();
+ double absNrmX, absNrmY, absNrmZ, pD = 0.0;
+ Vector3d tempV3d = new Vector3d();
+ Vector3d direction = new Vector3d();
+ double pNrmDotrDir = 0.0;
+ int axis, nc, sh, nsh;
+ Point3d start = new Point3d();
+ Point3d end = new Point3d();
+
+ Point3d iPnt = new Point3d(); // Point of intersection.
+
+ double uCoor[] = new double[4]; // Only need to support up to quad.
+ double vCoor[] = new double[4];
+ double tempD;
+
+ int i, j;
+
+ // Compute plane normal.
+ for(i=0; i<coordinates.length-1;) {
+ vec0.x = coordinates[i+1].x - coordinates[i].x;
+ vec0.y = coordinates[i+1].y - coordinates[i].y;
+ vec0.z = coordinates[i+1].z - coordinates[i++].z;
+ if(vec0.length() > 0.0)
+ break;
+ }
+
+ for(j=i; j<coordinates.length-1; j++) {
+ vec1.x = coordinates[j+1].x - coordinates[j].x;
+ vec1.y = coordinates[j+1].y - coordinates[j].y;
+ vec1.z = coordinates[j+1].z - coordinates[j].z;
+ if(vec1.length() > 0.0)
+ break;
+ }
+
+ if(j == (coordinates.length-1)) {
+ // System.out.println("(1) Degenerated polygon.");
+ return false; // Degenerated polygon.
+ }
+
+ /*
+ System.out.println("Triangle/Quad :");
+ for(i=0; i<coordinates.length; i++)
+ System.out.println("P" + i + " " + coordinates[i]);
+ */
+
+ pNrm.cross(vec0,vec1);
+
+ if(pNrm.length() == 0.0) {
+ // System.out.println("(2) Degenerated polygon.");
+ return false; // Degenerated polygon.
+ }
+ // Compute plane D.
+ tempV3d.set((Tuple3d) coordinates[0]);
+ pD = pNrm.dot(tempV3d);
+
+ segment.get(start, end);
+ // System.out.println("Segment start : " + start + " end " + end);
+
+ direction.x = end.x - start.x;
+ direction.y = end.y - start.y;
+ direction.z = end.z - start.z;
+
+ pNrmDotrDir = pNrm.dot(direction);
+
+ // Segment is parallel to plane.
+ if(pNrmDotrDir== 0.0) {
+ // System.out.println("Segment is parallel to plane.");
+ return false;
+ }
+
+ tempV3d.set((Tuple3d) start);
+
+ dist[0] = (pD - pNrm.dot(tempV3d))/ pNrmDotrDir;
+
+ // Segment intersects the plane behind the segment's start.
+ // or exceed the segment's length.
+ if((dist[0] < 0.0 ) || (dist[0] > 1.0 )) {
+ // System.out.println("Segment intersects the plane behind the start or exceed end.");
+ return false;
+ }
+
+ // Now, one thing for sure the segment intersect the plane.
+ // Find the intersection point.
+ iPnt.x = start.x + direction.x * dist[0];
+ iPnt.y = start.y + direction.y * dist[0];
+ iPnt.z = start.z + direction.z * dist[0];
+
+ // System.out.println("dist " + dist[0] + " iPnt : " + iPnt);
+
+ // Project 3d points onto 2d plane and apply Jordan curve theorem.
+ // Note : Area of polygon is not preserve in this projection, but
+ // it doesn't matter here.
+
+ // Find the axis of projection.
+ absNrmX = Math.abs(pNrm.x);
+ absNrmY = Math.abs(pNrm.y);
+ absNrmZ = Math.abs(pNrm.z);
+
+ if(absNrmX > absNrmY)
+ axis = 0;
+ else
+ axis = 1;
+
+ if(axis == 0) {
+ if(absNrmX < absNrmZ)
+ axis = 2;
+ }
+ else if(axis == 1) {
+ if(absNrmY < absNrmZ)
+ axis = 2;
+ }
+
+ // System.out.println("Normal " + pNrm + " axis " + axis );
+
+ for(i=0; i<coordinates.length; i++) {
+ switch (axis) {
+ case 0:
+ uCoor[i] = coordinates[i].y - iPnt.y;
+ vCoor[i] = coordinates[i].z - iPnt.z;
+ break;
+
+ case 1:
+ uCoor[i] = coordinates[i].x - iPnt.x;
+ vCoor[i] = coordinates[i].z - iPnt.z;
+ break;
+
+ case 2:
+ uCoor[i] = coordinates[i].x - iPnt.x;
+ vCoor[i] = coordinates[i].y - iPnt.y;
+ break;
+ }
+
+ // System.out.println("i " + i + " u " + uCoor[i] + " v " + vCoor[i]);
+ }
+
+ // initialize number of crossing, nc.
+ nc = 0;
+
+ if(vCoor[0] < 0.0)
+ sh = -1;
+ else
+ sh = 1;
+
+ for(i=0; i<coordinates.length; i++) {
+ j= i+1;
+ if(j==coordinates.length)
+ j=0;
+
+ if(vCoor[j] < 0.0)
+ nsh = -1;
+ else
+ nsh = 1;
+
+ if(sh != nsh) {
+ if((uCoor[i] > 0.0) && (uCoor[j] > 0.0)) {
+ // This line must cross U+.
+ nc++;
+ }
+ else if((uCoor[i] > 0.0) || (uCoor[j] > 0.0)) {
+ // This line might cross U+. We need to compute intersection on U azis.
+ tempD = uCoor[i]-vCoor[i]*(uCoor[j]-uCoor[i])/(vCoor[j]-vCoor[i]);
+ if(tempD > 0)
+ // This line cross U+.
+ nc++;
+ }
+ sh = nsh;
+ } // sh != nsh
+ }
+
+ // System.out.println("nc " + nc);
+
+ if((nc%2) == 1) {
+
+ // calculate the distance
+ dist[0] *= direction.length();
+
+ // System.out.println("Segment Intersected!");
+ /*
+ System.out.println("Segment orgin : " + start + " dir " + direction);
+ System.out.println("Triangle/Quad :");
+ for(i=0; i<coordinates.length; i++)
+ System.out.println("P" + i + " " + coordinates[i]);
+ System.out.println("dist " + dist[0] + " iPnt : " + iPnt);
+ */
+ return true;
+ }
+ else {
+ // System.out.println("Segment Not Intersected!");
+ return false;
+ }
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/picking/PickMouseBehavior.java b/src/classes/share/com/sun/j3d/utils/behaviors/picking/PickMouseBehavior.java
new file mode 100644
index 0000000..fbb1df5
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/picking/PickMouseBehavior.java
@@ -0,0 +1,152 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.picking;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+
+/*
+ * Base class that allows users to adding picking and mouse manipulation to
+ * his scene graph (see PickDragBehavior for an example of how to extend
+ * this base class). This class is useful for interactive apps.
+ */
+
+/**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>com.sun.j3d.utils.picking.behaviors.PickMouseBehavior</code>
+ *
+ * @see com.sun.j3d.utils.picking.behaviors.PickMouseBehavior
+ */
+
+public abstract class PickMouseBehavior extends Behavior {
+
+ /**
+ * Portion of the scene graph to operate picking on.
+ */
+ protected PickObject pickScene;
+
+ protected WakeupCriterion[] conditions;
+ protected WakeupOr wakeupCondition;
+ protected boolean buttonPress = false;
+
+ protected TransformGroup currGrp;
+ protected static final boolean debug = false;
+ protected MouseEvent mevent;
+
+ /**
+ * Creates a PickMouseBehavior given current canvas, root of the tree to
+ * operate on, and the bounds.
+ */
+ public PickMouseBehavior(Canvas3D canvas, BranchGroup root, Bounds bounds){
+ super();
+ currGrp = new TransformGroup();
+ currGrp.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+ currGrp.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
+ root.addChild(currGrp);
+ pickScene = new PickObject(canvas, root);
+ }
+
+ public void initialize() {
+
+ conditions = new WakeupCriterion[2];
+ conditions[0] = new WakeupOnAWTEvent(Event.MOUSE_MOVE);
+ conditions[1] = new WakeupOnAWTEvent(Event.MOUSE_DOWN);
+ wakeupCondition = new WakeupOr(conditions);
+
+ wakeupOn(wakeupCondition);
+ }
+
+ private void processMouseEvent(MouseEvent evt) {
+ buttonPress = false;
+
+ if (evt.getID()==MouseEvent.MOUSE_PRESSED |
+ evt.getID()==MouseEvent.MOUSE_CLICKED) {
+ buttonPress = true;
+ return;
+ }
+ else if (evt.getID() == MouseEvent.MOUSE_MOVED) {
+ // Process mouse move event
+ }
+ }
+
+ public void processStimulus (Enumeration criteria) {
+ WakeupCriterion wakeup;
+ AWTEvent[] evt = null;
+ int xpos = 0, ypos = 0;
+
+ while(criteria.hasMoreElements()) {
+ wakeup = (WakeupCriterion)criteria.nextElement();
+ if (wakeup instanceof WakeupOnAWTEvent)
+ evt = ((WakeupOnAWTEvent)wakeup).getAWTEvent();
+ }
+
+ if (evt[0] instanceof MouseEvent){
+ mevent = (MouseEvent) evt[0];
+
+ if (debug)
+ System.out.println("got mouse event");
+ processMouseEvent((MouseEvent)evt[0]);
+ xpos = mevent.getPoint().x;
+ ypos = mevent.getPoint().y;
+ }
+
+ if (debug)
+ System.out.println("mouse position " + xpos + " " + ypos);
+
+ if (buttonPress){
+ updateScene(xpos, ypos);
+ }
+ wakeupOn (wakeupCondition);
+ }
+
+ /** Subclasses shall implement this update function
+ */
+ public abstract void updateScene(int xpos, int ypos);
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/picking/PickObject.java b/src/classes/share/com/sun/j3d/utils/behaviors/picking/PickObject.java
new file mode 100644
index 0000000..3a85bc8
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/picking/PickObject.java
@@ -0,0 +1,841 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+/*
+New Base level methods:
+
+ISSUE :
+ How about PickPoint and PickSegment ?
+
+DONE :
+ PickShape generatePickRay(int x, int y)
+
+ SceneGraphPath[] pickAll(int x, int y)
+ SceneGraphPath[] pickAllSorted(int x, int y)
+ SceneGraphPath pickAny(int x, int y)
+ SceneGraphPath pickClosest(int x, int y)
+
+ Node getPickedNode(SceneGraphPath, flag)
+ Node getPickedNode(SceneGraphPath, flag, int count)
+ where flag can be any combo of:
+ Group, Morph, Primitive, Shape3D,
+ TransformGroup, Switch
+
+
+TODO :
+ SceneGraphPath[] pickGeomAll(int x, int y)
+ SceneGraphPath[] pickGeomAllSorted(int x, int y)
+ SceneGraphPath pickGeomAny(int x, int y)
+ SceneGraphPath pickGeomClosest(int x, int y)
+
+ bool intersect(SceneGraphPath, PickShape)
+
+
+ Eventually:
+ getClosestVtx(ScenGraphPath, PickShape)
+
+
+Misc:
+ Mouse should stay on top of object it is dragging
+
+ */
+
+package com.sun.j3d.utils.behaviors.picking;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.media.j3d.*;
+import com.sun.j3d.utils.geometry.Primitive;
+import javax.vecmath.*;
+
+/*
+ * Contains methods to aid in picking. A PickObject is created
+ * for a given Canvas3D and a BranchGroup. SceneGraphObjects
+ * under the specified BranchGroup can then be checked to determine
+ * if they have been picked.
+ */
+
+/**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>com.sun.j3d.utils.picking.PickCanvas</code>
+ *
+ * @see com.sun.j3d.utils.picking.PickCanvas
+ */
+
+public class PickObject extends Object {
+
+ // Have to rethink what to support. Is this complete.
+
+ /**
+ * A flag to indicate to the pickNode method to return a
+ * <code>Shape3D</code> node from
+ * a given <code>SceneGraphPath</code>.
+ *
+ * @see PickObject#pickNode
+ */
+ public static final int SHAPE3D = 0x1;
+
+ /**
+ * A flag to indicate to the pickNode method to return a
+ * <code>Morph</code> node from
+ * a given <code>SceneGraphPath</code>.
+ *
+ * @see PickObject#pickNode
+ */
+ public static final int MORPH = 0x2;
+
+ /**
+ * A flag to indicate to the pickNode method to return a
+ * <code>Primitive</code> node
+ * from a given <code>SceneGraphPath</code>.
+ *
+ * @see PickObject#pickNode
+ */
+ public static final int PRIMITIVE = 0x4;
+
+ /**
+ * A flag to indicate to the pickNode method to return a
+ * <code>Link</code> node from
+ * a given <code>SceneGraphPath</code>.
+ *
+ * @see PickObject#pickNode
+ */
+ public static final int LINK = 0x8;
+
+ /**
+ * A flag to indicate to the pickNode method to return a
+ * <code>Group</code> node from
+ * a given <code>SceneGraphPath</code>.
+ *
+ * @see PickObject#pickNode
+ */
+ public static final int GROUP = 0x10;
+
+ /**
+ * A flag to indicate to the pickNode method to return a
+ * <code>TransformGroup</code>
+ * node from a given <code>SceneGraphPath</code>.
+ *
+ * @see PickObject#pickNode
+ */
+ public static final int TRANSFORM_GROUP = 0x20;
+
+ /**
+ * A flag to indicate to the pickNode method to return a
+ * <code>BranchGroup</code>
+ * node from a given <code>SceneGraphPath</code>.
+ *
+ * @see PickObject#pickNode
+ */
+ public static final int BRANCH_GROUP = 0x40;
+
+ /**
+ * A flag to indicate to the pickNode method to return a
+ * <code>Switch</code> node from
+ * a given <code>SceneGraphPath</code>.
+ *
+ * @see PickObject#pickNode
+ */
+ public static final int SWITCH = 0x80;
+
+
+ /**
+ * Set this flag if you want to pick by geometry.
+ */
+ public static final int USE_GEOMETRY = 0x100;
+
+
+ /**
+ * Set this flag if you want to pick by bounds.
+ */
+ public static final int USE_BOUNDS = 0x200;
+
+ BranchGroup pickRoot;
+ Canvas3D canvas;
+ Point3d origin = new Point3d();
+ Vector3d direction = new Vector3d();
+ PickRay pickRay = new PickRay();
+ SceneGraphPath sceneGraphPath = null;
+ SceneGraphPath sceneGraphPathArr[] = null;
+ int pickBy; // To pick by Bounds or Geometry.
+
+ static final boolean debug = false;
+
+ /**
+ * Creates a PickObject.
+ * @param c Current J3D canvas.
+ * @param root The portion of the scenegraph for which picking is to occur
+ * on. It has to be a <code>BranchGroup</code>.
+ *
+ * @see BranchGroup
+ * @see Canvas3D
+ */
+ public PickObject(Canvas3D c, BranchGroup root)
+ {
+ pickRoot = root;
+ canvas = c;
+ }
+
+ /**
+ * Creates a PickRay that starts at the viewer position and points into
+ * the scene in the direction of (xpos, ypos) specified in window space.
+ *
+ * @param xpos The value along the x-axis.
+ * @param ypos The value along the y-axis.
+ * @return A PickShape object that is the constructed PickRay.
+ */
+ public PickShape generatePickRay(int xpos, int ypos)
+ {
+
+ Transform3D motion=new Transform3D();
+ Point3d eyePosn = new Point3d();
+ Point3d mousePosn = new Point3d();
+ Vector3d mouseVec=new Vector3d();
+
+ canvas.getCenterEyeInImagePlate(eyePosn);
+ canvas.getPixelLocationInImagePlate(xpos,ypos,mousePosn);
+ if (canvas.getView().getProjectionPolicy() ==
+ View.PARALLEL_PROJECTION) {
+ // Correct for the parallel projection: keep the eye's z
+ // coordinate, but make x,y be the same as the mouse, this
+ // simulates the eye being at "infinity"
+ eyePosn.x = mousePosn.x;
+ eyePosn.y = mousePosn.y;
+ }
+
+ canvas.getImagePlateToVworld(motion);
+
+ if (debug) {
+ System.out.println("mouse position " + xpos + " " + ypos);
+ System.out.println("before, mouse " + mousePosn + " eye " + eyePosn);
+ }
+
+ motion.transform(eyePosn);
+ motion.transform(mousePosn);
+ mouseVec.sub(mousePosn, eyePosn);
+ mouseVec.normalize();
+
+ if (debug) {
+ System.out.println(motion + "\n");
+ System.out.println("after, mouse " + mousePosn + " eye " + eyePosn +
+ " mouseVec " + mouseVec);
+ }
+
+ pickRay.set(eyePosn, mouseVec);
+
+ return (PickShape) pickRay;
+
+ }
+
+ /**
+ * Returns an array referencing all the items that are pickable below the
+ * <code>BranchGroup</code> (specified in the PickObject constructor) that
+ * intersect with a ray that starts at the
+ * viewer position and points into the scene in the direction of (xpos, ypos)
+ * specified in window space. The resultant array is unordered.
+ *
+ * @param xpos The value along the x-axis.
+ * @param ypos The value along the y-axis.
+ * @return The array of SceneGraphPath objects that contain Objects that
+ * were picked
+ * If no pickable object is found <code>null</code> is returned..
+ *
+ * @see SceneGraphPath
+ */
+ public SceneGraphPath[] pickAll(int xpos, int ypos)
+ {
+ pickRay = (PickRay) generatePickRay(xpos, ypos);
+ sceneGraphPathArr = pickRoot.pickAll(pickRay);
+ return sceneGraphPathArr;
+ }
+
+ /**
+ * Returns a sorted array of references to all the Pickable items below the
+ * <code>BranchGroup</code> (specified in the PickObject constructor) that
+ * intersect with the ray that starts at the viewer
+ * position and points into the scene in the direction of (xpos, ypos)
+ * in the window space.
+ * Element [0] references the item closest to viewer.
+ *
+ * @param xpos The value along the x-axis.
+ * @param ypos The value along the y-axis.
+ * @return A sorted arrayof SceneGraphPath objects that contain Objects that
+ * were picked. The array is sorted from closest to farthest from the
+ * viewer
+ * If no pickable object is found <code>null</code> is returned..
+ *
+ * @see SceneGraphPath
+ */
+ public SceneGraphPath[] pickAllSorted(int xpos, int ypos)
+ {
+ pickRay = (PickRay) generatePickRay(xpos, ypos);
+ sceneGraphPathArr = pickRoot.pickAllSorted(pickRay);
+ return sceneGraphPathArr;
+ }
+
+ /**
+ * Returns a reference to any item that is Pickable below the specified
+ * <code>BranchGroup</code> (specified in the PickObject constructor) which
+ * intersects with the ray that starts at the viewer
+ * position and points into the scene in the direction of (xpos, ypos) in
+ * window space.
+ *
+ * @param xpos The value along the x-axis.
+ * @param ypos The value along the y-axis.
+ * @return A SceneGraphPath of an object that was picked. This is not
+ * guarenteed to return the same result for multiple picks
+ * If no pickable object is found <code>null</code> is returned..
+ *
+ * @see SceneGraphPath
+ */
+ public SceneGraphPath pickAny(int xpos, int ypos)
+ {
+ pickRay = (PickRay) generatePickRay(xpos, ypos);
+ sceneGraphPath = pickRoot.pickAny(pickRay);
+ return sceneGraphPath;
+ }
+
+ /**
+ * Returns a reference to the item that is closest to the viewer and is
+ * Pickable below the <code>BranchGroup</code> (specified in the PickObject
+ * constructor) which intersects with the ray that starts at
+ * the viewer position and points into the scene in the direction of
+ * (xpos, ypos) in the window space.
+ *
+ * @param xpos The value along the x-axis.
+ * @param ypos The value along the y-axis.
+ * @return A SceneGraphPath which contains the closest pickable object.
+ * If no pickable object is found, <code>null</code> is returned.
+ *
+ * @see SceneGraphPath
+ */
+ public SceneGraphPath pickClosest(int xpos, int ypos)
+ {
+ pickRay = (PickRay) generatePickRay(xpos, ypos);
+ sceneGraphPath = pickRoot.pickClosest(pickRay);
+ return sceneGraphPath;
+ }
+
+
+ /**
+ * Returns an array referencing all the items that are pickable below the
+ * <code>BranchGroup</code> (specified in the PickObject constructor) that
+ * intersect with a ray that starts at the
+ * viewer position and points into the scene in the direction of (xpos, ypos)
+ * specified in window space. The resultant array is unordered.
+ *
+ * @param xpos The value along the x-axis.
+ * @param ypos The value along the y-axis.
+ * @param flag Specifys picking by Geometry or Bounds.
+ * @return The array of SceneGraphPath objects that contain Objects that
+ * were picked
+ * If no pickable object is found <code>null</code> is returned..
+ *
+ * @see SceneGraphPath
+ */
+ public SceneGraphPath[] pickAll(int xpos, int ypos, int flag)
+ {
+
+ if(flag == USE_BOUNDS) {
+ return pickAll(xpos, ypos);
+ }
+ else if(flag == USE_GEOMETRY) {
+ return pickGeomAll(xpos, ypos);
+ }
+ else
+ return null;
+ }
+
+ /**
+ * Returns a sorted array of references to all the Pickable items below the
+ * <code>BranchGroup</code> (specified in the PickObject constructor) that
+ * intersect with the ray that starts at the viewer
+ * position and points into the scene in the direction of (xpos, ypos)
+ * in the window space.
+ * Element [0] references the item closest to viewer.
+ *
+ * @param xpos The value along the x-axis.
+ * @param ypos The value along the y-axis.
+ * @param flag Specifys picking by Geometry or Bounds.
+ * @return A sorted arrayof SceneGraphPath objects that contain Objects that
+ * were picked. The array is sorted from closest to farthest from the
+ * viewer
+ * If no pickable object is found <code>null</code> is returned..
+ *
+ * @see SceneGraphPath
+ */
+ public SceneGraphPath[] pickAllSorted(int xpos, int ypos, int flag)
+ {
+
+ if(flag == USE_BOUNDS) {
+ return pickAllSorted(xpos, ypos);
+ }
+ else if(flag == USE_GEOMETRY) {
+ return pickGeomAllSorted(xpos, ypos);
+ }
+ else
+ return null;
+
+ }
+
+ /**
+ * Returns a reference to any item that is Pickable below the specified
+ * <code>BranchGroup</code> (specified in the PickObject constructor) which
+ * intersects with the ray that starts at the viewer
+ * position and points into the scene in the direction of (xpos, ypos) in
+ * window space.
+ *
+ * @param xpos The value along the x-axis.
+ * @param ypos The value along the y-axis.
+ * @param flag Specifys picking by Geometry or Bounds.
+ * @return A SceneGraphPath of an object that was picked. This is not
+ * guarenteed to return the same result for multiple picks
+ * If no pickable object is found <code>null</code> is returned..
+ *
+ * @see SceneGraphPath
+ */
+ public SceneGraphPath pickAny(int xpos, int ypos, int flag)
+ {
+
+ if(flag == USE_BOUNDS) {
+ return pickAny(xpos, ypos);
+ }
+ else if(flag == USE_GEOMETRY) {
+ return pickGeomAny(xpos, ypos);
+ }
+ else
+ return null;
+ }
+
+ /**
+ * Returns a reference to the item that is closest to the viewer and is
+ * Pickable below the <code>BranchGroup</code> (specified in the PickObject
+ * constructor) which intersects with the ray that starts at
+ * the viewer position and points into the scene in the direction of
+ * (xpos, ypos) in the window space.
+ *
+ * @param xpos The value along the x-axis.
+ * @param ypos The value along the y-axis.
+ * @param flag Specifys picking by Geometry or Bounds.
+ * @return A SceneGraphPath which contains the closest pickable object.
+ * If no pickable object is found, <code>null</code> is returned.
+ *
+ * @see SceneGraphPath
+ */
+ public SceneGraphPath pickClosest(int xpos, int ypos, int flag)
+ {
+
+ if(flag == USE_BOUNDS) {
+ return pickClosest(xpos, ypos);
+ }
+ else if(flag == USE_GEOMETRY) {
+ return pickGeomClosest(xpos, ypos);
+ }
+ else
+ return null;
+ }
+
+ private SceneGraphPath[] pickGeomAll(int xpos, int ypos)
+ {
+ Node obj;
+ int i, cnt=0;
+
+ pickRay = (PickRay) generatePickRay(xpos, ypos);
+ sceneGraphPathArr = pickRoot.pickAll(pickRay);
+
+ if(sceneGraphPathArr == null)
+ return null;
+
+ boolean found[] = new boolean[sceneGraphPathArr.length];
+
+ for(i=0; i<sceneGraphPathArr.length; i++) {
+ obj = sceneGraphPathArr[i].getObject();
+ if(obj instanceof Shape3D) {
+ found[i] = ((Shape3D) obj).intersect(sceneGraphPathArr[i],
+ (PickShape) pickRay);
+ } else if(obj instanceof Morph) {
+ found[i] = ((Morph) obj).intersect(sceneGraphPathArr[i],
+ (PickShape) pickRay);
+ }
+ if(found[i] == true)
+ cnt++;
+ }
+
+ if(cnt == 0)
+ return null;
+
+ SceneGraphPath newSceneGraphPathArr[] = new SceneGraphPath[cnt];
+
+ cnt = 0; // reset for reuse.
+ for(i=0; i<sceneGraphPathArr.length; i++) {
+ if(found[i] == true)
+ newSceneGraphPathArr[cnt++] = sceneGraphPathArr[i];
+ }
+
+ return newSceneGraphPathArr;
+ }
+
+ private double distance[];
+
+ private SceneGraphPath[] pickGeomAllSorted(int xpos, int ypos)
+ {
+ Node obj;
+ int i, cnt=0;
+ double dist[] = new double[1];
+
+ // System.out.print("In pickGeomAllSorted\n");
+ pickRay = (PickRay) generatePickRay(xpos, ypos);
+ sceneGraphPathArr = pickRoot.pickAll(pickRay);
+
+ if(sceneGraphPathArr == null)
+ return null;
+
+ boolean found[] = new boolean[sceneGraphPathArr.length];
+ double distArr[] = new double[sceneGraphPathArr.length];
+
+ for(i=0; i<sceneGraphPathArr.length; i++) {
+ obj = sceneGraphPathArr[i].getObject();
+ if(obj instanceof Shape3D) {
+ found[i] = ((Shape3D) obj).intersect(sceneGraphPathArr[i],
+ pickRay, dist);
+ distArr[i] = dist[0];
+ } else if(obj instanceof Morph) {
+ found[i] = ((Morph) obj).intersect(sceneGraphPathArr[i],
+ pickRay, dist);
+ distArr[i] = dist[0];
+ }
+ if(found[i] == true)
+ cnt++;
+ }
+
+ if(cnt == 0)
+ return null;
+
+ SceneGraphPath newSceneGraphPathArr[] = new SceneGraphPath[cnt];
+ distance = new double[cnt];
+
+ cnt = 0; // reset for reuse.
+ for(i=0; i<sceneGraphPathArr.length; i++) {
+ if(found[i] == true) {
+ newSceneGraphPathArr[cnt] = sceneGraphPathArr[i];
+ distance[cnt++] = distArr[i];
+ }
+ }
+
+ return sort(newSceneGraphPathArr);
+ }
+
+
+ private SceneGraphPath pickGeomClosest(int xpos, int ypos)
+ {
+ SceneGraphPath sgpArr[] = pickGeomAllSorted(xpos, ypos);
+
+ if (sgpArr == null)
+ return null;
+
+ return sgpArr[0];
+ }
+
+
+ private SceneGraphPath pickGeomAny(int xpos, int ypos)
+ {
+ Node obj;
+ int i;
+
+ pickRay = (PickRay) generatePickRay(xpos, ypos);
+ sceneGraphPathArr = pickRoot.pickAll(pickRay);
+ for(i=0; i<sceneGraphPathArr.length; i++) {
+ obj = sceneGraphPathArr[i].getObject();
+ if(obj instanceof Shape3D) {
+ if(((Shape3D) obj).intersect(sceneGraphPathArr[i],(PickShape) pickRay))
+ return sceneGraphPathArr[i];
+ } else if(obj instanceof Morph) {
+ if(((Morph) obj).intersect(sceneGraphPathArr[i],(PickShape) pickRay))
+ return sceneGraphPathArr[i];
+ }
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Sort the elements in sgpArr and
+ * return the sorted list in SceneGraphPath
+ *
+ * Sorts on the distance but also adjusts an array of positions
+ * this allows the sort to operate on small data elements rather
+ * than the possibly large SceneGraphPath
+ *
+ * Initial implementation is a Quick Sort
+ */
+
+ private int position[];
+
+ private SceneGraphPath[] sort(SceneGraphPath sgpArr[]) {
+
+ if (sgpArr == null)
+ return null;
+
+ SceneGraphPath sorted[] = new SceneGraphPath[sgpArr.length];
+ position = new int[sgpArr.length];
+
+ for(int i=0; i<sgpArr.length; i++) {
+ position[i]=i;
+ }
+
+
+ /*
+ System.out.println("Before Sort :");
+ for(int i=0; i<distance.length; i++) {
+ System.out.println("pos " + position[i] +" dist "+ distance[i] +
+ " sgp "+ sgpArr[i]);
+ }
+ */
+
+ quicksort( 0, distance.length-1 );
+
+ for(int i=0; i<distance.length; i++) {
+ sorted[i]= sgpArr[position[i]];
+ }
+
+ /*
+ System.out.println("\nAfter Sort :");
+ for(int i=0; i<distance.length; i++) {
+ System.out.println("pos " + position[i] +" dist "+ distance[i] +
+ " sorted sgp "+ sorted[i]);
+ }
+ */
+
+ return sorted;
+ }
+
+
+ private final void quicksort( int l, int r ) {
+ int p,i,j;
+ double tmp,k;
+
+ i = l;
+ j = r;
+ k = distance[(l+r) / 2];
+ do {
+ while (distance[i]<k) i++;
+ while (k<distance[j]) j--;
+ if (i<=j) {
+ tmp = distance[i];
+ distance[i] =distance[j];
+ distance[j] = tmp;
+
+ p=position[i];
+ position[i]=position[j];
+ position[j]=p;
+ i++;
+ j--;
+ }
+ } while (i<=j);
+
+ if (l<j) quicksort(l,j);
+ if (l<r) quicksort(i,r);
+ }
+
+
+ /**
+ * Returns a reference to a Pickable Node that
+ * is of the specified type
+ * that is contained in the specified SceneGraphPath.
+ * If more than one node of the same type is encountered, the node
+ * closest to the terminal node of SceneGraphPath will be returned.
+ *
+ * @param sgPath the SceneGraphPath to be traversed.
+ * @param flags the Node types interested in picking.
+ * @return the first occurrence of the specified Node type
+ * starting from the terminal node of SceneGraphPath.
+ * If no pickable object is found of the specifed types,
+ * <code>null</code> is returned.
+ */
+ public Node pickNode(SceneGraphPath sgPath, int flags)
+ {
+
+ if (sgPath != null) {
+ Node pickedNode = sgPath.getObject();
+
+ if ((pickedNode instanceof Shape3D) && ((flags & SHAPE3D) != 0)){
+ if (debug) System.out.println("Shape3D found");
+ return pickedNode;
+ }
+ else if ((pickedNode instanceof Morph) && ((flags & MORPH) != 0)){
+ if (debug) System.out.println("Morph found");
+ return pickedNode;
+ }
+ else {
+ for (int j=sgPath.nodeCount()-1; j>=0; j--){
+ pickedNode = sgPath.getNode(j);
+ if (debug) System.out.println("looking at node " + pickedNode);
+
+ if ((pickedNode instanceof Primitive) &&
+ ((flags & PRIMITIVE) != 0)){
+ if (debug) System.out.println("Primitive found");
+ return pickedNode;
+ }
+ else if ((pickedNode instanceof Link) && ((flags & LINK) != 0)){
+ if (debug) System.out.println("Link found");
+ return pickedNode;
+ }
+ else if ((pickedNode instanceof Switch) && ((flags & SWITCH) != 0)){
+ if (debug) System.out.println("Switch found");
+ return pickedNode;
+ }
+ else if ((pickedNode instanceof TransformGroup) &&
+ ((flags & TRANSFORM_GROUP) != 0)){
+ if (debug) System.out.println("xform group found");
+ return pickedNode;
+ }
+ else if ((pickedNode instanceof BranchGroup) &&
+ ((flags & BRANCH_GROUP) != 0)){
+ if (debug) System.out.println("Branch group found");
+ return pickedNode;
+ }
+ else if ((pickedNode instanceof Group) && ((flags & GROUP) != 0)){
+ if (debug) System.out.println("Group found");
+ return pickedNode;
+ }
+ }
+
+ if (pickedNode == null)
+ if (debug) System.out.println("ERROR: null SceneGraphPath");
+ }
+
+ }
+
+ return null;
+
+ }
+
+
+ /**
+ * Returns a reference to a Pickable Node that
+ * is of the specified type
+ * that is contained in the specified SceneGraphPath.
+ * The Node returned is the nth <code>occurrence</code>
+ * of a Node that is of the specified type.
+ *
+ * @param sgPath the SceneGraphPath to be traversed.
+ * @param flags the Node types interested.
+ * @param occurrence the occurrence of a Node that
+ * matches the specified type to return. An <code>occurrence</code> of
+ * 1 means to return the first occurrence of that object type (the object
+ * closest to the Locale).
+ * @return the nth <code>occurrence</code> of a Node
+ * of type <code>flags</code>, starting from the Locale. If no pickable object is
+ * found, <code>null</code> is returned.
+ */
+ public Node pickNode(SceneGraphPath sgPath, int flags, int occurrence)
+ {
+ int curCnt=0;
+
+ if (sgPath != null) {
+ Node pickedNode = sgPath.getObject();
+
+ // Shape3D and Morph are leaf nodes and have no children. It doesn't
+ // make sense to do occurrence check here. We'll just return it for now.
+ if ((pickedNode instanceof Shape3D) && ((flags & SHAPE3D) != 0)){
+ if (debug) System.out.println("Shape3D found");
+ return pickedNode;
+ } else if ((pickedNode instanceof Morph) && ((flags & MORPH) != 0)){
+ if (debug) System.out.println("Morph found");
+ return pickedNode;
+ }
+ else {
+ for (int j = 0; j < sgPath.nodeCount(); j++){
+ pickedNode = sgPath.getNode(j);
+ if (debug) System.out.println("looking at node " + pickedNode);
+
+ if ((pickedNode instanceof Group) && ((flags & GROUP) != 0)){
+ if (debug) System.out.println("Group found");
+ curCnt++;
+ if(curCnt == occurrence)
+ return pickedNode;
+ }
+ else if ((pickedNode instanceof BranchGroup) &&
+ ((flags & BRANCH_GROUP) != 0)){
+ if (debug) System.out.println("Branch group found");
+ curCnt++;
+ if(curCnt == occurrence)
+ return pickedNode;
+ }
+ else if ((pickedNode instanceof TransformGroup) &&
+ ((flags & TRANSFORM_GROUP) != 0)){
+ if (debug) System.out.println("xform group found");
+ curCnt++;
+ if(curCnt == occurrence)
+ return pickedNode;
+ }
+ else if ((pickedNode instanceof Primitive) &&
+ ((flags & PRIMITIVE) != 0)){
+ if (debug) System.out.println("Primitive found");
+ curCnt++;
+ if(curCnt == occurrence)
+ return pickedNode;
+ }
+ else if ((pickedNode instanceof Link) && ((flags & LINK) != 0)){
+ if (debug) System.out.println("Link found");
+ curCnt++;
+ if(curCnt == occurrence)
+ return pickedNode;
+ }
+ }
+
+ if (pickedNode == null)
+ if (debug) System.out.println("ERROR: null SceneGraphPath");
+ }
+
+ }
+
+ return null;
+
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/picking/PickRotateBehavior.java b/src/classes/share/com/sun/j3d/utils/behaviors/picking/PickRotateBehavior.java
new file mode 100644
index 0000000..a51fa18
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/picking/PickRotateBehavior.java
@@ -0,0 +1,194 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.picking;
+
+import com.sun.j3d.utils.behaviors.mouse.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+/*
+ * A mouse behavior that allows user to pick and drag scene graph objects.
+ * Common usage:
+ * <p>
+ * 1. Create your scene graph.
+ * <p>
+ * 2. Create this behavior with root and canvas.
+ * <p>
+ * <blockquote><pre>
+ * PickRotateBehavior behavior = new PickRotateBehavior(canvas, root, bounds);
+ * root.addChild(behavior);
+ * </pre></blockquote>
+ * <p>
+ * The above behavior will monitor for any picking events on
+ * the scene graph (below root node) and handle mouse drags on pick hits.
+ * Note the root node can also be a subgraph node of the scene graph (rather
+ * than the topmost).
+ */
+
+/**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>com.sun.j3d.utils.picking.behaviors.PickRotateBehavior</code>
+ *
+ * @see com.sun.j3d.utils.picking.behaviors.PickRotateBehavior
+ */
+
+public class PickRotateBehavior extends PickMouseBehavior implements MouseBehaviorCallback {
+ MouseRotate drag;
+ int pickMode = PickObject.USE_BOUNDS;
+ private PickingCallback callback=null;
+ private TransformGroup currentTG;
+
+ /**
+ * Creates a pick/rotate behavior that waits for user mouse events for
+ * the scene graph. This method has its pickMode set to BOUNDS picking.
+ * @param root Root of your scene graph.
+ * @param canvas Java 3D drawing canvas.
+ * @param bounds Bounds of your scene.
+ **/
+
+ public PickRotateBehavior(BranchGroup root, Canvas3D canvas, Bounds bounds){
+ super(canvas, root, bounds);
+ drag = new MouseRotate(MouseRotate.MANUAL_WAKEUP);
+ drag.setTransformGroup(currGrp);
+ currGrp.addChild(drag);
+ drag.setSchedulingBounds(bounds);
+ this.setSchedulingBounds(bounds);
+ }
+
+ /**
+ * Creates a pick/rotate behavior that waits for user mouse events for
+ * the scene graph.
+ * @param root Root of your scene graph.
+ * @param canvas Java 3D drawing canvas.
+ * @param bounds Bounds of your scene.
+ * @param pickMode specifys PickObject.USE_BOUNDS or PickObject.USE_GEOMETRY.
+ * Note: If pickMode is set to PickObject.USE_GEOMETRY, all geometry object in
+ * the scene graph that allows pickable must have its ALLOW_INTERSECT bit set.
+ **/
+
+ public PickRotateBehavior(BranchGroup root, Canvas3D canvas, Bounds bounds,
+ int pickMode){
+ super(canvas, root, bounds);
+ drag = new MouseRotate(MouseRotate.MANUAL_WAKEUP);
+ drag.setTransformGroup(currGrp);
+ currGrp.addChild(drag);
+ drag.setSchedulingBounds(bounds);
+ this.setSchedulingBounds(bounds);
+ this.pickMode = pickMode;
+ }
+
+ /**
+ * Sets the pickMode component of this PickRotateBehavior to the value of
+ * the passed pickMode.
+ * @param pickMode the pickMode to be copied.
+ **/
+
+
+ public void setPickMode(int pickMode) {
+ this.pickMode = pickMode;
+ }
+
+ /**
+ * Return the pickMode component of this PickRotateBehavior.
+ **/
+
+ public int getPickMode() {
+ return pickMode;
+ }
+
+ /**
+ * Update the scene to manipulate any nodes. This is not meant to be
+ * called by users. Behavior automatically calls this. You can call
+ * this only if you know what you are doing.
+ *
+ * @param xpos Current mouse X pos.
+ * @param ypos Current mouse Y pos.
+ **/
+ public void updateScene(int xpos, int ypos){
+ TransformGroup tg = null;
+
+ if (!mevent.isMetaDown() && !mevent.isAltDown()){
+
+ // tg = (TransformGroup) pickScene.pickNode(pickScene.pickClosest(xpos, ypos),
+ // PickObject.TRANSFORM_GROUP);
+
+ tg =(TransformGroup)pickScene.pickNode(pickScene.pickClosest(xpos, ypos,pickMode),
+ PickObject.TRANSFORM_GROUP);
+ // Make sure the selection exists and is movable.
+ if ((tg != null) &&
+ (tg.getCapability(TransformGroup.ALLOW_TRANSFORM_READ)) &&
+ (tg.getCapability(TransformGroup.ALLOW_TRANSFORM_WRITE))){
+ drag.setTransformGroup(tg);
+ drag.wakeup();
+ currentTG = tg;
+ } else if (callback!=null)
+ callback.transformChanged( PickingCallback.NO_PICK, null );
+ }
+ }
+
+ /**
+ * Callback method from MouseRotate
+ * This is used when the Picking callback is enabled
+ */
+ public void transformChanged( int type, Transform3D transform ) {
+ callback.transformChanged( PickingCallback.ROTATE, currentTG );
+ }
+
+ /**
+ * Register the class @param callback to be called each
+ * time the picked object moves
+ */
+ public void setupCallback( PickingCallback callback ) {
+ this.callback = callback;
+ if (callback==null)
+ drag.setupCallback( null );
+ else
+ drag.setupCallback( this );
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/picking/PickTranslateBehavior.java b/src/classes/share/com/sun/j3d/utils/behaviors/picking/PickTranslateBehavior.java
new file mode 100644
index 0000000..13f08d8
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/picking/PickTranslateBehavior.java
@@ -0,0 +1,178 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.picking;
+
+import com.sun.j3d.utils.behaviors.mouse.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+// A mouse behavior that allows user to pick and translate scene graph objects.
+// Common usage: 1. Create your scene graph. 2. Create this behavior with
+// the root and canvas. See PickRotateBehavior for more details.
+
+/**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>com.sun.j3d.utils.picking.behaviors.PickTranslateBehavior</code>
+ *
+ * @see com.sun.j3d.utils.picking.behaviors.PickTranslateBehavior
+ */
+
+public class PickTranslateBehavior extends PickMouseBehavior implements MouseBehaviorCallback {
+ MouseTranslate translate;
+ int pickMode = PickObject.USE_BOUNDS;
+ private PickingCallback callback = null;
+ private TransformGroup currentTG;
+
+ /**
+ * Creates a pick/translate behavior that waits for user mouse events for
+ * the scene graph. This method has its pickMode set to BOUNDS picking.
+ * @param root Root of your scene graph.
+ * @param canvas Java 3D drawing canvas.
+ * @param bounds Bounds of your scene.
+ **/
+
+ public PickTranslateBehavior(BranchGroup root, Canvas3D canvas, Bounds bounds){
+ super(canvas, root, bounds);
+ translate = new MouseTranslate(MouseBehavior.MANUAL_WAKEUP);
+ translate.setTransformGroup(currGrp);
+ currGrp.addChild(translate);
+ translate.setSchedulingBounds(bounds);
+ this.setSchedulingBounds(bounds);
+ }
+
+ /**
+ * Creates a pick/translate behavior that waits for user mouse events for
+ * the scene graph.
+ * @param root Root of your scene graph.
+ * @param canvas Java 3D drawing canvas.
+ * @param bounds Bounds of your scene.
+ * @param pickMode specifys PickObject.USE_BOUNDS or PickObject.USE_GEOMETRY.
+ * Note: If pickMode is set to PickObject.USE_GEOMETRY, all geometry object in
+ * the scene graph that allows pickable must have its ALLOW_INTERSECT bit set.
+ **/
+
+ public PickTranslateBehavior(BranchGroup root, Canvas3D canvas, Bounds bounds,
+ int pickMode){
+ super(canvas, root, bounds);
+ translate = new MouseTranslate(MouseBehavior.MANUAL_WAKEUP);
+ translate.setTransformGroup(currGrp);
+ currGrp.addChild(translate);
+ translate.setSchedulingBounds(bounds);
+ this.setSchedulingBounds(bounds);
+ this.pickMode = pickMode;
+ }
+
+ /**
+ * Sets the pickMode component of this PickTranslateBehavior to the value of
+ * the passed pickMode.
+ * @param pickMode the pickMode to be copied.
+ **/
+
+ public void setPickMode(int pickMode) {
+ this.pickMode = pickMode;
+ }
+
+ /**
+ * Return the pickMode component of this PickTranslaeBehavior.
+ **/
+
+ public int getPickMode() {
+ return pickMode;
+ }
+
+ /**
+ * Update the scene to manipulate any nodes. This is not meant to be
+ * called by users. Behavior automatically calls this. You can call
+ * this only if you know what you are doing.
+ *
+ * @param xpos Current mouse X pos.
+ * @param ypos Current mouse Y pos.
+ **/
+ public void updateScene(int xpos, int ypos){
+ TransformGroup tg = null;
+
+ if (!mevent.isAltDown() && mevent.isMetaDown()){
+
+ tg =(TransformGroup)pickScene.pickNode(pickScene.pickClosest(xpos, ypos, pickMode),
+ PickObject.TRANSFORM_GROUP);
+ //Check for valid selection.
+ if ((tg != null) &&
+ (tg.getCapability(TransformGroup.ALLOW_TRANSFORM_READ)) &&
+ (tg.getCapability(TransformGroup.ALLOW_TRANSFORM_WRITE))){
+
+ translate.setTransformGroup(tg);
+ translate.wakeup();
+ currentTG = tg;
+ } else if (callback!=null)
+ callback.transformChanged( PickingCallback.NO_PICK, null );
+ }
+
+ }
+
+ /**
+ * Callback method from MouseTranslate
+ * This is used when the Picking callback is enabled
+ */
+ public void transformChanged( int type, Transform3D transform ) {
+ callback.transformChanged( PickingCallback.TRANSLATE, currentTG );
+ }
+
+ /**
+ * Register the class @param callback to be called each
+ * time the picked object moves
+ */
+ public void setupCallback( PickingCallback callback ) {
+ this.callback = callback;
+ if (callback==null)
+ translate.setupCallback( null );
+ else
+ translate.setupCallback( this );
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/picking/PickZoomBehavior.java b/src/classes/share/com/sun/j3d/utils/behaviors/picking/PickZoomBehavior.java
new file mode 100644
index 0000000..a2159c7
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/picking/PickZoomBehavior.java
@@ -0,0 +1,180 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.picking;
+
+import com.sun.j3d.utils.behaviors.mouse.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+
+// A mouse behavior that allows user to pick and zoom scene graph objects.
+// Common usage: 1. Create your scene graph. 2. Create this behavior with
+// the root and canvas. See PickRotateBehavior for more details.
+
+/**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>com.sun.j3d.utils.picking.behaviors.PickZoomBehavior</code>
+ *
+ * @see com.sun.j3d.utils.picking.behaviors.PickZoomBehavior
+ */
+
+public class PickZoomBehavior extends PickMouseBehavior implements MouseBehaviorCallback {
+ MouseZoom zoom;
+ int pickMode = PickObject.USE_BOUNDS;
+ private PickingCallback callback = null;
+ private TransformGroup currentTG;
+
+ /**
+ * Creates a pick/zoom behavior that waits for user mouse events for
+ * the scene graph. This method has its pickMode set to BOUNDS picking.
+ * @param root Root of your scene graph.
+ * @param canvas Java 3D drawing canvas.
+ * @param bounds Bounds of your scene.
+ **/
+
+ public PickZoomBehavior(BranchGroup root, Canvas3D canvas, Bounds bounds){
+ super(canvas, root, bounds);
+ zoom = new MouseZoom(MouseBehavior.MANUAL_WAKEUP);
+ zoom.setTransformGroup(currGrp);
+ currGrp.addChild(zoom);
+ zoom.setSchedulingBounds(bounds);
+ this.setSchedulingBounds(bounds);
+ }
+
+ /**
+ * Creates a pick/zoom behavior that waits for user mouse events for
+ * the scene graph.
+ * @param root Root of your scene graph.
+ * @param canvas Java 3D drawing canvas.
+ * @param bounds Bounds of your scene.
+ * @param pickMode specifys PickObject.USE_BOUNDS or PickObject.USE_GEOMETRY.
+ * Note: If pickMode is set to PickObject.USE_GEOMETRY, all geometry object in
+ * the scene graph that allows pickable must have its ALLOW_INTERSECT bit set.
+ **/
+
+ public PickZoomBehavior(BranchGroup root, Canvas3D canvas, Bounds bounds,
+ int pickMode){
+ super(canvas, root, bounds);
+ zoom = new MouseZoom(MouseBehavior.MANUAL_WAKEUP);
+ zoom.setTransformGroup(currGrp);
+ currGrp.addChild(zoom);
+ zoom.setSchedulingBounds(bounds);
+ this.setSchedulingBounds(bounds);
+ this.pickMode = pickMode;
+ }
+
+ /**
+ * Sets the pickMode component of this PickZoomBehavior to the value of
+ * the passed pickMode.
+ * @param pickMode the pickMode to be copied.
+ **/
+
+ public void setPickMode(int pickMode) {
+ this.pickMode = pickMode;
+ }
+
+
+ /**
+ * Return the pickMode component of this PickZoomBehavior.
+ **/
+
+ public int getPickMode() {
+ return pickMode;
+ }
+
+
+ /**
+ * Update the scene to manipulate any nodes. This is not meant to be
+ * called by users. Behavior automatically calls this. You can call
+ * this only if you know what you are doing.
+ *
+ * @param xpos Current mouse X pos.
+ * @param ypos Current mouse Y pos.
+ **/
+
+ public void updateScene(int xpos, int ypos){
+ TransformGroup tg = null;
+
+ if (mevent.isAltDown() && !mevent.isMetaDown()){
+
+ tg =(TransformGroup)pickScene.pickNode(pickScene.pickClosest(xpos, ypos, pickMode),
+ PickObject.TRANSFORM_GROUP);
+
+ // Check for valid selection
+ if ((tg != null) &&
+ (tg.getCapability(TransformGroup.ALLOW_TRANSFORM_READ)) &&
+ (tg.getCapability(TransformGroup.ALLOW_TRANSFORM_WRITE))){
+ zoom.setTransformGroup(tg);
+ zoom.wakeup();
+ currentTG = tg;
+ } else if (callback!=null)
+ callback.transformChanged( PickingCallback.NO_PICK, null );
+ }
+ }
+
+ /**
+ * Callback method from MouseZoom
+ * This is used when the Picking callback is enabled
+ */
+ public void transformChanged( int type, Transform3D transform ) {
+ callback.transformChanged( PickingCallback.ZOOM, currentTG );
+ }
+
+ /**
+ * Register the class @param callback to be called each
+ * time the picked object moves
+ */
+ public void setupCallback( PickingCallback callback ) {
+ this.callback = callback;
+ if (callback==null)
+ zoom.setupCallback( null );
+ else
+ zoom.setupCallback( this );
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/picking/PickingCallback.java b/src/classes/share/com/sun/j3d/utils/behaviors/picking/PickingCallback.java
new file mode 100644
index 0000000..d873f2b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/picking/PickingCallback.java
@@ -0,0 +1,73 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.picking;
+
+import javax.media.j3d.TransformGroup;
+
+/**
+ * @deprecated As of Java 3D version 1.2, replaced by
+ * <code>com.sun.j3d.utils.picking.behaviors.PickingCallback</code>
+ *
+ * @see com.sun.j3d.utils.picking.behaviors.PickingCallback
+ */
+
+public interface PickingCallback {
+
+ public final static int ROTATE=0;
+ public final static int TRANSLATE=1;
+ public final static int ZOOM=2;
+
+ /**
+ * The user made a selection but nothing was
+ * actually picked
+ */
+ public final static int NO_PICK=3;
+
+ /**
+ * Called by the Pick Behavior with which this callback
+ * is registered each time the Picked object is moved
+ */
+ public void transformChanged( int type, TransformGroup tg );
+}
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/sensor/Mouse6DPointerBehavior.java b/src/classes/share/com/sun/j3d/utils/behaviors/sensor/Mouse6DPointerBehavior.java
new file mode 100644
index 0000000..c4f1e61
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/sensor/Mouse6DPointerBehavior.java
@@ -0,0 +1,195 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.sensor ;
+
+import java.util.Enumeration ;
+import javax.media.j3d.* ;
+import javax.vecmath.Point3d ;
+import javax.vecmath.Vector3f ;
+
+/**
+ * This class provides basic behavior for a 6DOF mouse sensor. It
+ * generates a visible 3D cursor echo in the virtual world which tracks the
+ * position and orientation of the 6DOF mouse in the physical world. It
+ * can be extended to provide other functions by accessing its
+ * SensorEventAgent.
+ *
+ * @see SensorEventAgent
+ * @since Java 3D 1.3
+ */
+public class Mouse6DPointerBehavior extends Behavior {
+ private Sensor sensor = null ;
+ private SensorEventAgent eventAgent = null ;
+ private TransformGroup echoTransformGroup = null ;
+ private WakeupCondition conditions = new WakeupOnElapsedFrames(0) ;
+
+ /**
+ * Constructs the behavior with a default echo. To make the echo visible,
+ * call getEcho() to retrieve the TransformGroup that parents the echo
+ * geometry, and then add that TransformGroup to the scene graph.
+ * <p>
+ * The default echo is a solid 6-pointed star where each point is aligned
+ * with the axes of the local coordinate system of the sensor, and with
+ * the center of the star at the location of the sensor hotspot.
+ *
+ * @param sensor a 6 degree of freedom Sensor which generates position
+ * and orientation relative to the tracker base.
+ * @param size the physical width of the echo in centimeters.
+ * @param enableLighting a boolean indicating whether the echo geometry
+ * should have lighting enabled.
+ */
+ public Mouse6DPointerBehavior(Sensor sensor, double size,
+ boolean enableLighting) {
+
+ this.sensor = sensor ;
+ echoTransformGroup = new TransformGroup() ;
+ echoTransformGroup.setCapability
+ (TransformGroup.ALLOW_TRANSFORM_WRITE) ;
+
+ Point3d hotspot = new Point3d() ;
+ sensor.getHotspot(hotspot) ;
+
+ Transform3D t3d = new Transform3D() ;
+ Vector3f v3f = new Vector3f(hotspot) ;
+ t3d.set(v3f) ;
+
+ Shape3D echo =
+ new SensorGnomonEcho(t3d, 0.001*size, 0.005*size, enableLighting) ;
+ echoTransformGroup.addChild(echo) ;
+
+ eventAgent = new SensorEventAgent(this) ;
+ eventAgent.addSensorReadListener(sensor, new EchoReadListener()) ;
+ }
+
+ /**
+ * Constructs the behavior with an echo parented by the specified
+ * TransformGroup.
+ *
+ * @param sensor a 6 degree of freedom Sensor which generates position
+ * and orientation relative to the tracker base.
+ * @param tg a TransformGroup with a child defining the visible echo
+ * which will track the Sensor position and orientation; the Transform3D
+ * associated with the TransformGroup will be updated in order to effect
+ * the behavior, so it must have the ALLOW_TRANSFORM_WRITE capability
+ * set before the scene graph is set live
+ */
+ public Mouse6DPointerBehavior(Sensor sensor, TransformGroup tg) {
+ this.sensor = sensor ;
+ echoTransformGroup = tg ;
+ eventAgent = new SensorEventAgent(this) ;
+ eventAgent.addSensorReadListener(sensor, new EchoReadListener()) ;
+ }
+
+ /**
+ * Gets the sensor used by this behavior.
+ *
+ * @return the sensor used by this behavior
+ */
+ public Sensor getSensor() {
+ return sensor ;
+ }
+
+ /**
+ * Gets the echo used by this behavior.
+ *
+ * @return the TransformGroup parenting this behavior's echo geometry
+ */
+ public TransformGroup getEcho() {
+ return echoTransformGroup ;
+ }
+
+ /**
+ * Gets the SensorEventAgent used by this behavior. This can be used to
+ * add customized event bindings to this behavior.
+ *
+ * @return the SensorEventAgent
+ */
+ public SensorEventAgent getSensorEventAgent() {
+ return eventAgent ;
+ }
+
+ /**
+ * Initializes the behavior.
+ * NOTE: Applications should not call this method. It is called by the
+ * Java 3D behavior scheduler.
+ */
+ public void initialize() {
+ wakeupOn(conditions) ;
+ }
+
+ /**
+ * Processes a stimulus meant for this behavior.
+ * NOTE: Applications should not call this method. It is called by the
+ * Java 3D behavior scheduler.
+ */
+ public void processStimulus(Enumeration criteria) {
+ eventAgent.dispatchEvents() ;
+ wakeupOn(conditions) ;
+ }
+
+ /**
+ * This member class updates the echo transform in response to sensor
+ * reads.
+ */
+ public class EchoReadListener implements SensorReadListener {
+ private Transform3D t3d = new Transform3D() ;
+
+ public void read(SensorEvent e) {
+ // Get the Transform3D that transforms points from local sensor
+ // coordinates to virtual world coordinates, based on the primary
+ // view associated with this Behavior. This view is defined to be
+ // the first View attached to a live ViewPlatform.
+ //
+ // Note that this will display frame lag if another behavior such
+ // as OrbitBehavior is used to manipulate the view transform while
+ // the echo is visible. In order to eliminate frame lag the
+ // behavior driving the view transform must also compute the echo
+ // transform as well. See the WandViewBehavior utility for the
+ // appropriate techniques.
+ getView().getSensorToVworld(e.getSensor(), t3d) ;
+ echoTransformGroup.setTransform(t3d) ;
+ }
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorBeamEcho.java b/src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorBeamEcho.java
new file mode 100644
index 0000000..387ec5c
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorBeamEcho.java
@@ -0,0 +1,231 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.sensor ;
+
+import javax.media.j3d.Shape3D ;
+import javax.media.j3d.Material ;
+import javax.media.j3d.Appearance ;
+import javax.media.j3d.Transform3D ;
+import javax.media.j3d.GeometryArray ;
+import javax.media.j3d.TriangleStripArray ;
+import javax.media.j3d.TransparencyAttributes;
+import javax.vecmath.AxisAngle4f ;
+import javax.vecmath.Point3d ;
+import javax.vecmath.Point3f ;
+import javax.vecmath.Vector3f ;
+
+/**
+ * A Shape3D representing a beam pointing from the origin of a
+ * sensor's local coordinate system to its hotspot.
+ *
+ * @since Java 3D 1.3
+ */
+public class SensorBeamEcho extends Shape3D {
+ /**
+ * Creates a SensorBeamEcho. Read and write capabilities are granted
+ * for the Appearance, Material, TransparencyAttributes, and
+ * TransparencyAttributes mode and value.
+ *
+ * @param hotspot location of the sensor's hotspot in the sensor's
+ * local coordinate system; this must not be (0, 0, 0)
+ * @param baseWidth width of the beam in meters
+ * @param enableLighting boolean indicating whether normals should be
+ * generated and lighting enabled
+ * @exception IllegalArgumentException if hotspot is (0, 0, 0)
+ */
+ public SensorBeamEcho(Point3d hotspot, double baseWidth,
+ boolean enableLighting) {
+ super() ;
+
+ if (hotspot.distance(new Point3d()) == 0.0)
+ throw new IllegalArgumentException
+ ("\nBeam echo can't have hotspot at origin") ;
+
+ Vector3f axis = new Vector3f((float)hotspot.x,
+ (float)hotspot.y,
+ (float)hotspot.z) ;
+
+ Vector3f axis1 = new Vector3f() ;
+ axis1.normalize(axis) ;
+
+ // Choose an arbitrary vector normal to the beam axis.
+ Vector3f normal = new Vector3f(0.0f, 1.0f, 0.0f) ;
+ normal.cross(axis1, normal) ;
+ if (normal.lengthSquared() < 0.5f) {
+ normal.set(0.0f, 0.0f, 1.0f) ;
+ normal.cross(axis1, normal) ;
+ }
+ normal.normalize() ;
+
+ // Create cap vertices and normals.
+ int divisions = 18 ;
+ Point3f[] cap0 = new Point3f[divisions] ;
+ Point3f[] cap1 = new Point3f[divisions] ;
+ Vector3f[] capNormals = new Vector3f[divisions] ;
+ Vector3f cap0Normal = new Vector3f(axis1) ;
+ Vector3f cap1Normal = new Vector3f(axis1) ;
+ cap0Normal.negate() ;
+
+ AxisAngle4f aa4f = new AxisAngle4f
+ (axis1, -(float)Math.PI/((float)divisions/2.0f)) ;
+ Transform3D t3d = new Transform3D() ;
+ t3d.set(aa4f) ;
+
+ float halfWidth = (float)baseWidth / 2.0f ;
+ for (int i = 0 ; i < divisions ; i++) {
+ capNormals[i] = new Vector3f(normal) ;
+ cap0[i] = new Point3f(normal) ;
+ cap0[i].scale(halfWidth) ;
+ cap1[i] = new Point3f(cap0[i]) ;
+ cap1[i].add(axis) ;
+ t3d.transform(normal) ;
+ }
+
+ // The beam cylinder is created with 3 triangle strips. The first
+ // strip contains the side facets (2 + 2*divisions vertices), and
+ // the other two strips are the caps (divisions vertices each).
+ int vertexCount = 2 + (4 * divisions) ;
+ Point3f[] vertices = new Point3f[vertexCount] ;
+ Vector3f[] normals = new Vector3f[vertexCount] ;
+
+ // Side facets.
+ for (int i = 0 ; i < divisions ; i++) {
+ vertices[i*2] = cap0[i] ;
+ vertices[(i*2) + 1] = cap1[i] ;
+
+ normals[i*2] = capNormals[i] ;
+ normals[(i*2) + 1] = capNormals[i] ;
+ }
+
+ vertices[divisions*2] = cap0[0] ;
+ vertices[(divisions*2) + 1] = cap1[0] ;
+
+ normals[divisions*2] = capNormals[0] ;
+ normals[(divisions*2) + 1] = capNormals[0] ;
+
+ // Strips for caps created by criss-crossing the interior.
+ int v = (divisions+1) * 2 ;
+ vertices[v] = cap0[0] ;
+ normals[v++] = cap0Normal ;
+
+ int j = 1 ;
+ int k = divisions - 1 ;
+ while (j <= k) {
+ vertices[v] = cap0[j++] ;
+ normals[v++] = cap0Normal ;
+ if (j > k) break ;
+ vertices[v] = cap0[k--] ;
+ normals[v++] = cap0Normal ;
+ }
+
+ vertices[v] = cap1[0] ;
+ normals[v++] = cap1Normal ;
+
+ j = 1 ;
+ k = divisions - 1 ;
+ while (j <= k) {
+ vertices[v] = cap1[k--] ;
+ normals[v++] = cap1Normal ;
+ if (j > k) break ;
+ vertices[v] = cap1[j++] ;
+ normals[v++] = cap1Normal ;
+ }
+
+ // Create the TriangleStripArray.
+ int vertexFormat ;
+ Material m = new Material() ;
+ m.setCapability(Material.ALLOW_COMPONENT_READ) ;
+ m.setCapability(Material.ALLOW_COMPONENT_WRITE) ;
+
+ if (enableLighting) {
+ vertexFormat =
+ GeometryArray.COORDINATES | GeometryArray.NORMALS ;
+ m.setLightingEnable(true) ;
+ }
+ else {
+ vertexFormat = GeometryArray.COORDINATES ;
+ m.setLightingEnable(false) ;
+ }
+
+ int[] stripCounts = new int[3] ;
+ stripCounts[0] = 2 + (2 * divisions) ;
+ stripCounts[1] = divisions ;
+ stripCounts[2] = divisions ;
+
+ TriangleStripArray tsa =
+ new TriangleStripArray(vertexCount,
+ vertexFormat, stripCounts) ;
+
+ tsa.setCoordinates(0, vertices) ;
+ if (enableLighting)
+ tsa.setNormals(0, normals) ;
+
+ Appearance a = new Appearance() ;
+ a.setMaterial(m) ;
+ a.setCapability(Appearance.ALLOW_MATERIAL_READ) ;
+ a.setCapability(Appearance.ALLOW_MATERIAL_WRITE) ;
+
+ TransparencyAttributes ta = new TransparencyAttributes() ;
+ ta.setCapability(TransparencyAttributes.ALLOW_MODE_READ) ;
+ ta.setCapability(TransparencyAttributes.ALLOW_MODE_WRITE) ;
+ ta.setCapability(TransparencyAttributes.ALLOW_VALUE_READ) ;
+ ta.setCapability(TransparencyAttributes.ALLOW_VALUE_WRITE) ;
+ ta.setCapability
+ (TransparencyAttributes.ALLOW_BLEND_FUNCTION_READ) ;
+ ta.setCapability
+ (TransparencyAttributes.ALLOW_BLEND_FUNCTION_WRITE) ;
+
+ a.setTransparencyAttributes(ta) ;
+ a.setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_READ) ;
+ a.setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE) ;
+
+ setGeometry(tsa) ;
+ setAppearance(a) ;
+
+ setCapability(ALLOW_APPEARANCE_READ) ;
+ setCapability(ALLOW_APPEARANCE_WRITE) ;
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorButtonListener.java b/src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorButtonListener.java
new file mode 100644
index 0000000..121f05b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorButtonListener.java
@@ -0,0 +1,94 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.sensor ;
+
+/**
+ * This defines the interface for handling a sensor's button events in
+ * conjunction with a <code>SensorEventAgent</code> instance.
+ * <p>
+ * The events passed to this listener's methods are <i>ephemeral</i>; they
+ * are only valid until the listener has returned. If a listener needs to
+ * retain the event it must be copied using the
+ * <code>SensorEvent(SensorEvent)</code> constructor.
+ *
+ * @see SensorEvent
+ * @see SensorEventAgent
+ * @see SensorReadListener
+ * @since Java 3D 1.3
+ */
+public interface SensorButtonListener {
+ /**
+ * This method is called when a sensor's button is pressed.
+ *
+ * @param e the sensor event
+ */
+ public void pressed(SensorEvent e) ;
+
+ /**
+ * This method is called when a sensor's button is released.
+ *
+ * @param e the sensor event
+ */
+ public void released(SensorEvent e) ;
+
+ /**
+ * This method is called with each invocation of the
+ * <code>dispatchEvents</code> method of <code>SensorEventAgent</code>
+ * if any button bound to the listener is down and has not changed
+ * state since the last invocation. The sensor value has not
+ * necessarily changed from the last drag event.
+ *
+ * @param e the sensor event
+ */
+ public void dragged(SensorEvent e) ;
+
+ /**
+ * This method is currently not used by <code>SensorEventAgent</code>,
+ * but is included here for future possible development. Its
+ * implementations should remain empty for the present.
+ */
+ public void clicked(SensorEvent e) ;
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorEvent.java b/src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorEvent.java
new file mode 100644
index 0000000..de0a65d
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorEvent.java
@@ -0,0 +1,336 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.sensor ;
+
+import javax.media.j3d.Sensor ;
+import javax.media.j3d.Transform3D ;
+
+/**
+ * This class defines the event object that is created by a
+ * <code>SensorEventAgent</code> and passed to registered
+ * <code>SensorReadListener</code> and <code>SensorButtonListener</code>
+ * implementations.
+ * <p>
+ * The events passed to the listeners are <i>ephemeral</i>; they are only
+ * valid until the listener has returned. This is done to avoid
+ * allocating large numbers of mostly temporary objects, especially for
+ * behaviors that wake up every frame. If a listener needs to retain the
+ * event it must be copied using the <code>SensorEvent(SensorEvent)</code>
+ * constructor.
+ *
+ * @see SensorEventAgent
+ * @see SensorButtonListener
+ * @see SensorReadListener
+ * @since Java 3D 1.3
+ */
+public class SensorEvent {
+ /**
+ * A button pressed event.
+ */
+ public static final int PRESSED = 1 ;
+
+ /**
+ * A button released event.
+ */
+ public static final int RELEASED = 2 ;
+
+ /**
+ * A button dragged event.
+ */
+ public static final int DRAGGED = 3 ;
+
+ /**
+ * A sensor read event.
+ */
+ public static final int READ = 4 ;
+
+ /**
+ * The value that is returned by <code>getButton</code> when no
+ * buttons have changed state.
+ */
+ public static final int NOBUTTON = -1 ;
+
+ private int id = 0 ;
+ private Object source = null ;
+ private Sensor sensor = null ;
+ private int button = NOBUTTON ;
+ private int[] buttonState = null ;
+ private Transform3D sensorRead = null ;
+ private long time = 0 ;
+ private long lastTime = 0 ;
+ private boolean ephemeral = false ;
+
+ /**
+ * Creates a new <code>SensorEvent</code>.
+ *
+ * @param source a reference to the originating object which
+ * instantiated the <code>SensorEventAgent</code>, usually a
+ * <code>Behavior</code>; may be null
+ * @param id event type
+ * @param sensor a reference to the provoking sensor
+ * @param sensorRead the sensor's read value at the time of the event
+ * @param buttonState the state of the sensor's buttons at the time of
+ * the event, where a 1 in the array indicates that the button at that
+ * index is down, and a 0 indicates that button is up; may be null
+ * @param button index of the button that changed state, from 0 to
+ * <code>(buttonCount - 1)</code>, or the value <code>NOBUTTON</code>
+ * @param time the time in nanoseconds at which the
+ * <code>dispatchEvents</code> method of
+ * <code>SensorEventAgent</code> was called to generate this event,
+ * usually from the <code>processStimulus</code> method of a Behavior
+ * @param lastTime the time in nanoseconds at which the
+ * <code>dispatchEvents</code> method of
+ * <code>SensorEventAgent</code> was <i>last</i> called to generate
+ * events, usually from the <code>processStimulus</code> method of a
+ * <code>Behavior</code>; may be used to measure frame time in
+ * behaviors that wake up every frame
+ */
+ public SensorEvent(Object source, int id, Sensor sensor,
+ Transform3D sensorRead, int[] buttonState,
+ int button, long time, long lastTime) {
+
+ this.source = source ;
+ this.id = id ;
+ this.sensor = sensor ;
+ this.button = button ;
+ this.time = time ;
+ this.lastTime = lastTime ;
+ if (sensorRead == null)
+ throw new NullPointerException("sensorRead can't be null") ;
+ this.sensorRead = new Transform3D(sensorRead) ;
+ if (buttonState != null) {
+ this.buttonState = new int[buttonState.length] ;
+ for (int i = 0 ; i < buttonState.length ; i++)
+ this.buttonState[i] = buttonState[i] ;
+ }
+ this.ephemeral = false ;
+ }
+
+ /**
+ * Creates a new <i>ephemeral</i> <code>SensorEvent</code>. In order
+ * to avoid creating large numbers of sensor event objects, the events
+ * passed to the button and read listeners by the
+ * <code>dispatchEvents</code> method of <code>SensorEventAgent</code>
+ * are valid only until the listener returns. If the event needs to
+ * be retained then they must be copied with the
+ * <code>SensorEvent(SensorEvent)</code> constructor.
+ */
+ public SensorEvent() {
+ this.ephemeral = true ;
+ }
+
+ /**
+ * Creates a copy of the given <code>SensorEvent</code>. Listeners
+ * must use this constructor to copy events that need to be retained.
+ * NOTE: The <code>Sensor</code> and <code>Object</code> references
+ * returned by <code>getSensor</code> and <code>getSource</code>
+ * remain references to the original objects.
+ *
+ * @param e the event to be copied
+ */
+ public SensorEvent(SensorEvent e) {
+ this.source = e.source ;
+ this.id = e.id ;
+ this.sensor = e.sensor ;
+ this.button = e.button ;
+ this.time = e.time ;
+ this.lastTime = e.lastTime ;
+ if (e.sensorRead == null)
+ throw new NullPointerException("sensorRead can't be null") ;
+ this.sensorRead = new Transform3D(e.sensorRead) ;
+ if (e.buttonState != null) {
+ this.buttonState = new int[e.buttonState.length] ;
+ for (int i = 0 ; i < e.buttonState.length ; i++)
+ this.buttonState[i] = e.buttonState[i] ;
+ }
+ this.ephemeral = false ;
+ }
+
+ /**
+ * Sets the fields of an ephemeral event. No objects are copied. An
+ * <code>IllegalStateException</code> will be thrown if this event
+ * is not ephemeral.
+ *
+ * @param source a reference to the originating object which
+ * instantiated the <code>SensorEventAgent</code>, usually a
+ * <code>Behavior</code>; may be null
+ * @param id event type
+ * @param sensor a reference to the provoking sensor
+ * @param sensorRead the sensor's read value at the time of the event
+ * @param buttonState the state of the sensor's buttons at the time of
+ * the event; a 1 in the array indicates that the button at that
+ * index is down, while a 0 indicates that button is up
+ * @param button index of the button that changed state, from 0 to
+ * <code>(buttonCount - 1)</code>, or the value <code>NOBUTTON</code>
+ * @param time the time in nanoseconds at which the
+ * <code>dispatchEvents</code> method of
+ * <code>SensorEventAgent</code> was called to generate this event,
+ * usually from the <code>processStimulus</code> method of a Behavior
+ * @param lastTime the time in nanoseconds at which the
+ * <code>dispatchEvents</code> method of
+ * <code>SensorEventAgent</code> was <i>last</i> called to generate
+ * events, usually from the <code>processStimulus</code> method of a
+ * <code>Behavior</code>; may be used to measure frame time in
+ * behaviors that wake up every frame
+ */
+ public void set(Object source, int id, Sensor sensor,
+ Transform3D sensorRead, int[] buttonState,
+ int button, long time, long lastTime) {
+
+ if (!ephemeral)
+ throw new IllegalStateException
+ ("Can't set the fields of non-ephemeral events") ;
+
+ this.source = source ;
+ this.id = id ;
+ this.sensor = sensor ;
+ if (sensorRead == null)
+ throw new NullPointerException("sensorRead can't be null") ;
+ this.sensorRead = sensorRead ;
+ this.buttonState = buttonState ;
+ this.button = button ;
+ this.time = time ;
+ this.lastTime = lastTime ;
+ }
+
+ /**
+ * Gets a reference to the originating object which instantiated the
+ * <code>SensorEventAgent</code>, usually a <code>Behavior</code>; may
+ * be null.
+ * @return the originating object
+ */
+ public Object getSource() {
+ return source ;
+ }
+
+ /**
+ * Gets the event type.
+ * @return the event id
+ */
+ public int getID() {
+ return id ;
+ }
+
+
+ /**
+ * Gets a reference to the provoking sensor.
+ * @return the provoking sensor
+ */
+ public Sensor getSensor() {
+ return sensor ;
+ }
+
+ /**
+ * Gets the time in nanoseconds at which the
+ * <code>dispatchEvents</code> method of <code>SensorEventAgent</code>
+ * was called to generate this event, usually from the
+ * <code>processStimulus</code> method of a <code>Behavior</code>.
+ * @return time in nanoseconds
+ */
+ public long getTime() {
+ return time ;
+ }
+
+ /**
+ * Gets the time in nanoseconds at which the
+ * <code>dispatchEvents</code> method of <code>SensorEventAgent</code>
+ * was <i>last</i> called to generate events, usually from the
+ * <code>processStimulus</code> method of a <code>Behavior</code>; may
+ * be used to measure frame time in behaviors that wake up every
+ * frame.
+ * @return last time in nanoseconds
+ */
+ public long getLastTime() {
+ return lastTime ;
+ }
+
+ /**
+ * Copies the sensor's read value at the time of the event into the
+ * given <code>Transform3D</code>.
+ *
+ * @param t the transform to receive the sensor read
+ */
+ public void getSensorRead(Transform3D t) {
+ t.set(sensorRead) ;
+ }
+
+ /**
+ * Gets the index of the button that changed state when passed to a
+ * <code>pressed</code> or <code>released</code> callback. The index
+ * may range from 0 to <code>(sensor.getSensorButtonCount() -
+ * 1)</code>. The value returned is <code>NOBUTTON</code> for events
+ * passed to a <code>read</code> or <code>dragged</code> callback.
+ * @return the button index
+ */
+ public int getButton() {
+ return button ;
+ }
+
+ /**
+ * Copies the state of the sensor's buttons at the time of the event
+ * into the given array. A 1 in the array indicates that the button
+ * at that index is down, while a 0 indicates that button is up.
+ * @param buttonState the state of the sensor buttons
+ */
+ public void getButtonState(int[] buttonState) {
+ if (buttonState.length != this.buttonState.length)
+ throw new ArrayIndexOutOfBoundsException
+ ("buttonState array is the wrong length") ;
+
+ for (int i = 0 ; i < buttonState.length ; i++)
+ buttonState[i] = this.buttonState[i] ;
+ }
+
+ /**
+ * Returns true if this event is <i>ephemeral</i> and is valid only
+ * until the listener returns. A copy of the event can be created by
+ * passing it to the <code>SensorEvent(SensorEvent)</code>
+ * constructor.
+ */
+ public boolean isEphemeral() {
+ return ephemeral ;
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorEventAgent.java b/src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorEventAgent.java
new file mode 100644
index 0000000..1c1a1bf
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorEventAgent.java
@@ -0,0 +1,715 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.sensor ;
+
+import java.util.Iterator ;
+import java.util.List ;
+import java.util.ArrayList ;
+import javax.media.j3d.Sensor ;
+import javax.media.j3d.Transform3D ;
+import com.sun.j3d.utils.timer.J3DTimer ;
+
+/**
+ * This class works in conjunction with the <code>SensorButtonListener</code>
+ * and <code>SensorReadListener</code> interfaces to support an event-driven
+ * model of sensor interaction. Java 3D defines sensors as delivering
+ * continuous input data which must be polled to retrieve their values, but in
+ * practice it is often convenient to structure application code to respond to
+ * events such as button state transitions.
+ * <p>
+ * Listeners registered with this class are invoked when its
+ * <code>dispatchEvents</code> method is called. This is usually called from
+ * the <code>processStimulus</code> method of a <code>Behavior</code>, but may
+ * also be called directly from the <code>pollAndProcessInput</code> method of
+ * an event-driven implementation of <code>InputDevice</code>. In either case
+ * the device is still polled by the Java 3D input device scheduling thread to
+ * get its current values; however, in the former, <code>dispatchEvents</code>
+ * is called from the behavior scheduler thread regardless of whether any new
+ * events are available, while in the latter, the <code>InputDevice</code>
+ * implementation may choose to call <code>dispatchEvents</code> only if new
+ * events are actually generated.
+ * <p>
+ * Button events are generated by changes in sensor button state, from pressed
+ * to released and vice versa. Button state changes are examined with each
+ * invocation of the <code>dispatchEvents</code> method. Events are
+ * distributed to interested parties through the button listener interface
+ * using the <code>pressed</code> and <code>released</code> callbacks.
+ * <p>
+ * The <code>dragged</code> method is not necessarily called in response to a
+ * motion event generated by a sensor. <code>dispatchEvents</code> will call
+ * <code>dragged</code> whenever any button assigned to the listener is down
+ * and has not changed state since the last time it was called. If
+ * <code>dispatchEvents</code> is called in the <code>processStimulus</code>
+ * of a <code>Behavior</code>, then <code>dragged</code> may be called even if
+ * the sensor value has not changed. This is as a consequence of the core
+ * Java 3D API definition of sensors as continuous devices.
+ * <p>
+ * Like <code>dragged</code>, the <code>read</code> method of
+ * <code>SensorReadListener</code> is not necessarily invoked in response to a
+ * real event. It is called by <code>dispatchEvents</code> whenever a button
+ * listener has not been called for that sensor. This usually means that no
+ * buttons are down, but clients are free to leave a button listener null, or
+ * to explicitly bind a null button listener to a button so that button's
+ * events are ignored. The sensor value has not necessarily changed since the
+ * last <code>read</code> callback.
+ * <p>
+ * A <i>mutual exclusion</i> policy can be applied between button
+ * listeners when they are grouped in an array mapped to the sensor's
+ * buttons. If multiple sensor buttons are held down at the same time,
+ * then a listener in the array is invoked only for the button that was
+ * depressed first. The <code>read</code> callback is separated from the
+ * <code>pressed</code>, <code>released</code>, and <code>dragged</code>
+ * callbacks in a separate interface in order to support this policy.
+ * <p>
+ * The events passed to the listeners are <i>ephemeral</i>; they are only
+ * valid until the listener has returned. This is done to avoid
+ * allocating large numbers of mostly temporary objects, especially for
+ * behaviors that wake up every frame. If a listener needs to retain the
+ * event it must be copied using the <code>SensorEvent(SensorEvent)</code>
+ * constructor.
+ * <p>
+ * It is safe to add and remove listeners in response to a callback.
+ *
+ * @see SensorEvent
+ * @see SensorButtonListener
+ * @see SensorReadListener
+ * @since Java 3D 1.3
+ */
+public class SensorEventAgent {
+ private long t0 = 0 ;
+ private Object source = null ;
+ private SensorEvent e = new SensorEvent() ;
+
+ // List of SensorBinding objects and corresponding array.
+ private List bindingsList = new ArrayList() ;
+ private SensorBinding[] bindings = new SensorBinding[0] ;
+
+ // Indicates that lists must be converted to arrays. Need to do this
+ // to allow listeners to add and remove themselves or other listeners
+ // safely during event dispatch.
+ private boolean listsDirty = false ;
+
+ /**
+ * Create a <code>SensorEventAgent</code> to generate and dispatch
+ * sensor events to registered listeners.
+ *
+ * @param source reference to the originating object for inclusion in
+ * generated <code>SensorEvents</code>; intended to refer to the
+ * instantiating Behavior but may be any reference, or null
+ */
+ public SensorEventAgent(Object source) {
+ this.source = source ;
+ }
+
+ /**
+ * This class contains all the button and read listeners registered
+ * with a sensor.
+ */
+ private static class SensorBinding {
+ Sensor sensor = null ;
+ int[] buttons = null ;
+ Transform3D read = null ;
+
+ // List of SensorButtonBinding objects and corresponding array.
+ List buttonBindingsList = new ArrayList() ;
+ SensorButtonBinding[] buttonBindings = new SensorButtonBinding[0] ;
+
+ // List of SensorReadListener objects and corresponding array.
+ List readBindingsList = new ArrayList() ;
+ SensorReadListener[] readBindings = new SensorReadListener[0] ;
+
+ SensorBinding(Sensor sensor) {
+ this.sensor = sensor ;
+ buttons = new int[sensor.getSensorButtonCount()] ;
+ read = new Transform3D() ;
+ }
+
+ void updateArrays() {
+ buttonBindings =
+ (SensorButtonBinding[])buttonBindingsList.toArray
+ (new SensorButtonBinding[buttonBindingsList.size()]) ;
+ readBindings =
+ (SensorReadListener[])readBindingsList.toArray
+ (new SensorReadListener[readBindingsList.size()]) ;
+ }
+
+ public String toString() {
+ String s = new String() ;
+ s = "sensor " + sensor + "\nbutton listener arrays:\n" ;
+ for (int i = 0 ; i < buttonBindingsList.size() ; i++)
+ s = s + ((SensorButtonBinding)buttonBindingsList.get(i)) ;
+ s = s + "read listeners:\n" ;
+ for (int i = 0 ; i < readBindingsList.size() ; i++)
+ s = s + " " +
+ ((SensorReadListener)readBindingsList.get(i)) + "\n" ;
+ return s ;
+ }
+ }
+
+ /**
+ * This class contains an array of SensorButtonListener
+ * implementations, one for each sensor button. This array is used to
+ * support a mutual exclusion callback policy. There may be multiple
+ * instances of this class associated with a single sensor.
+ */
+ private static class SensorButtonBinding {
+ int buttonsHandled = 0 ;
+ boolean[] prevButtons = null ;
+ boolean multiButton = false ;
+ SensorButtonListener[] listeners = null ;
+
+ SensorButtonBinding(SensorButtonListener[] listeners,
+ boolean multiButtonEnable) {
+
+ prevButtons = new boolean[listeners.length] ;
+ this.listeners = new SensorButtonListener[listeners.length] ;
+
+ for (int i = 0 ; i < listeners.length ; i++) {
+ prevButtons[i] = false ;
+ this.listeners[i] = listeners[i] ;
+ }
+
+ this.multiButton = multiButtonEnable ;
+ }
+
+ public String toString() {
+ String s = new String() ;
+ s = " length " + listeners.length +
+ ", mutual exclusion " + (!multiButton) + "\n" ;
+ for (int i = 0 ; i < listeners.length ; i++)
+ s = s + " " +
+ (listeners[i] == null?
+ "null" : listeners[i].toString()) + "\n" ;
+ return s ;
+ }
+ }
+
+ /**
+ * Look up the sensor listeners bound to the given sensor.
+ */
+ private SensorBinding getSensorBinding(Sensor sensor) {
+ for (int i = 0 ; i < bindingsList.size() ; i++) {
+ SensorBinding sb = (SensorBinding)bindingsList.get(i) ;
+ if (sb.sensor == sensor)
+ return sb ;
+ }
+ return null ;
+ }
+
+ /**
+ * Creates a binding of the specified sensor button to the given
+ * <code>SensorButtonListener</code> implementation.
+ *
+ * @param sensor the sensor with the button to be bound
+ * @param button the index of the button to be bound on the specified
+ * sensor; may range from 0 to
+ * <code>(sensor.getSensorButtonCount() - 1)</code>
+ * @param buttonListener the <code>SensorButtonListener</code>
+ * implementation that will be invoked for the sensor's button
+ */
+ public synchronized void addSensorButtonListener
+ (Sensor sensor, int button, SensorButtonListener buttonListener) {
+
+ if (sensor == null)
+ throw new NullPointerException("\nsensor is null") ;
+
+ if (button >= sensor.getSensorButtonCount())
+ throw new ArrayIndexOutOfBoundsException
+ ("\nbutton " + button + " >= sensor button count " +
+ sensor.getSensorButtonCount()) ;
+
+ SensorBinding sb = getSensorBinding(sensor) ;
+ if (sb == null) {
+ sb = new SensorBinding(sensor) ;
+ bindingsList.add(sb) ;
+ }
+
+ SensorButtonListener[] listeners =
+ new SensorButtonListener[sb.buttons.length] ;
+
+ // Assign only the specified button; others remain null.
+ listeners[button] = buttonListener ;
+ SensorButtonBinding sbb =
+ new SensorButtonBinding(listeners, true) ;
+
+ sb.buttonBindingsList.add(sbb) ;
+ listsDirty = true ;
+ }
+
+ /**
+ * Creates a binding from all the buttons on the specified sensor to
+ * the given <code>SensorButtonListener</code> implementation. If
+ * multiple sensor buttons are held down at the same time, the press
+ * and release callbacks are called for each button in the order that
+ * they occur. This allows actions to be bound to combinations of
+ * button presses, but is also convenient for listeners that don't
+ * care which button was pressed.
+ *
+ * @param sensor the sensor to be bound
+ * @param buttonListener the <code>SensorButtonListener</code>
+ * implementation that will be called for all button events
+ */
+ public synchronized void addSensorButtonListener
+ (Sensor sensor, SensorButtonListener buttonListener) {
+
+ if (sensor == null)
+ throw new NullPointerException("\nsensor is null") ;
+
+ SensorBinding sb = getSensorBinding(sensor) ;
+ if (sb == null) {
+ sb = new SensorBinding(sensor) ;
+ bindingsList.add(sb) ;
+ }
+
+ SensorButtonListener[] listeners =
+ new SensorButtonListener[sb.buttons.length] ;
+
+ // All buttons are bound to the same listener.
+ for (int i = 0 ; i < sb.buttons.length ; i++)
+ listeners[i] = buttonListener ;
+
+ SensorButtonBinding sbb =
+ new SensorButtonBinding(listeners, true) ;
+
+ sb.buttonBindingsList.add(sbb) ;
+ listsDirty = true ;
+ }
+
+ /**
+ * Creates a binding of the specified sensor to the given array of
+ * <code>SensorButtonListener</code> implementations. The array index
+ * of the listener indicates the index of the sensor button to which
+ * it will be bound.
+ * <p>
+ * This method enforces a <i>mutually exclusive</i> callback policy
+ * among the listeners specified in the array. If multiple sensor
+ * buttons are held down at the same time, callbacks are invoked only
+ * for the button that was depressed first.
+ *
+ * @param sensor the sensor to be bound
+ * @param buttonListeners array of implementations of
+ * <code>SensorButtonListener</code>; array entries may be null or
+ * duplicates but the array length must equal the sensor's button
+ * count
+ */
+ public synchronized void addSensorButtonListeners
+ (Sensor sensor, SensorButtonListener[] buttonListeners) {
+
+ if (sensor == null)
+ throw new NullPointerException("\nsensor is null") ;
+
+ SensorBinding sb = getSensorBinding(sensor) ;
+ if (sb == null) {
+ sb = new SensorBinding(sensor) ;
+ bindingsList.add(sb) ;
+ }
+
+ if (sb.buttons.length != buttonListeners.length)
+ throw new IllegalArgumentException
+ ("\nbuttonListeners length " + buttonListeners.length +
+ " must equal sensor button count " + sb.buttons.length) ;
+
+ SensorButtonBinding sbb =
+ new SensorButtonBinding(buttonListeners, false) ;
+
+ sb.buttonBindingsList.add(sbb) ;
+ listsDirty = true ;
+ }
+
+ /**
+ * Gets the <code>SensorButtonListener</code> implementations bound to
+ * the given sensor and button.
+ *
+ * @param sensor the sensor of interest
+ * @param button the button of interest
+ * @return array of <code>SensorButtonListener</code> implementations
+ * bound to the given sensor and button, or null
+ */
+ public SensorButtonListener[] getSensorButtonListeners(Sensor sensor,
+ int button) {
+ if (sensor == null)
+ throw new NullPointerException("\nsensor is null") ;
+
+ if (button >= sensor.getSensorButtonCount())
+ throw new ArrayIndexOutOfBoundsException
+ ("\nbutton " + button + " >= sensor button count " +
+ sensor.getSensorButtonCount()) ;
+
+ SensorBinding sb = getSensorBinding(sensor) ;
+ if (sb == null)
+ return null ;
+
+ ArrayList listeners = new ArrayList() ;
+ for (int i = 0 ; i < sb.buttonBindingsList.size() ; i++) {
+ SensorButtonBinding sbb =
+ (SensorButtonBinding)sb.buttonBindingsList.get(i) ;
+
+ if (sbb.listeners[button] != null)
+ listeners.add(sbb.listeners[button]) ;
+ }
+
+ if (listeners.size() == 0)
+ return null ;
+ else
+ return (SensorButtonListener[])listeners.toArray
+ (new SensorButtonListener[listeners.size()]) ;
+ }
+
+ /**
+ * Remove the SensorButtonListener from the given SensorBinding.
+ */
+ private void removeSensorButtonListener
+ (SensorBinding sb, SensorButtonListener listener) {
+
+ Iterator i = sb.buttonBindingsList.iterator() ;
+ while (i.hasNext()) {
+ int instanceCount = 0 ;
+ SensorButtonBinding sbb = (SensorButtonBinding)i.next() ;
+
+ for (int j = 0 ; j < sbb.listeners.length ; j++) {
+ if (sbb.listeners[j] == listener)
+ sbb.listeners[j] = null ;
+ else if (sbb.listeners[j] != null)
+ instanceCount++ ;
+ }
+ if (instanceCount == 0) {
+ i.remove() ;
+ }
+ }
+ listsDirty = true ;
+ }
+
+ /**
+ * Remove the given <code>SensorButtonListener</code> binding from the
+ * specified sensor.
+ *
+ * @param sensor the sensor from which to remove the listener
+ * @param listener the listener to be removed
+ */
+ public synchronized void removeSensorButtonListener
+ (Sensor sensor, SensorButtonListener listener) {
+
+ if (sensor == null)
+ throw new NullPointerException("\nsensor is null") ;
+
+ SensorBinding sb = getSensorBinding(sensor) ;
+ if (sb == null)
+ return ;
+
+ removeSensorButtonListener(sb, listener) ;
+ if (sb.buttonBindingsList.size() == 0 &&
+ sb.readBindingsList.size() == 0)
+ removeSensorBinding(sensor) ;
+
+ listsDirty = true ;
+ }
+
+ /**
+ * Remove the given <code>SensorButtonListener</code> from all sensors.
+ *
+ * @param listener the listener to remove
+ */
+ public synchronized void removeSensorButtonListener
+ (SensorButtonListener listener) {
+
+ Iterator i = bindingsList.iterator() ;
+ while (i.hasNext()) {
+ SensorBinding sb = (SensorBinding)i.next() ;
+ removeSensorButtonListener(sb, listener) ;
+
+ if (sb.buttonBindingsList.size() == 0 &&
+ sb.readBindingsList.size() == 0) {
+ i.remove() ;
+ }
+ }
+ listsDirty = true ;
+ }
+
+ /**
+ * Creates a binding of the specified sensor to the given
+ * <code>SensorReadListener</code>. The read listener is invoked
+ * every time <code>dispatchEvents</code> is called and a button
+ * listener is <i>not</i> invoked.
+ *
+ * @param sensor the sensor to be bound
+ * @param readListener the <code>SensorReadListener</code>
+ * implementation
+ */
+ public synchronized void addSensorReadListener
+ (Sensor sensor, SensorReadListener readListener) {
+
+ if (sensor == null)
+ throw new NullPointerException("\nsensor is null") ;
+
+ SensorBinding sb = getSensorBinding(sensor) ;
+ if (sb == null) {
+ sb = new SensorBinding(sensor) ;
+ bindingsList.add(sb) ;
+ }
+ sb.readBindingsList.add(readListener) ;
+ listsDirty = true ;
+ }
+
+ /**
+ * Gets the <code>SensorReadListeners</code> bound to the specified
+ * sensor.
+ *
+ * @param sensor the sensor of interest
+ * @return array of <code>SensorReadListeners</code> bound to the
+ * given sensor, or null
+ */
+ public SensorReadListener[] getSensorReadListeners(Sensor sensor) {
+ if (sensor == null)
+ throw new NullPointerException("\nsensor is null") ;
+
+ SensorBinding sb = getSensorBinding(sensor) ;
+ if (sb == null)
+ return null ;
+ else if (sb.readBindingsList.size() == 0)
+ return null ;
+ else
+ return (SensorReadListener[])sb.readBindingsList.toArray
+ (new SensorReadListener[sb.readBindingsList.size()]) ;
+ }
+
+ /**
+ * Remove the SensorReadListener from the given SensorBinding.
+ */
+ private void removeSensorReadListener
+ (SensorBinding sb, SensorReadListener listener) {
+
+ Iterator i = sb.readBindingsList.iterator() ;
+ while (i.hasNext()) {
+ if (((SensorReadListener)i.next()) == listener)
+ i.remove() ;
+ }
+ listsDirty = true ;
+ }
+
+ /**
+ * Remove the given <code>SensorReadListener</code> binding from the
+ * specified sensor.
+ *
+ * @param sensor the sensor from which to remove the listener
+ * @param listener the listener to be removed
+ */
+ public synchronized void removeSensorReadListener
+ (Sensor sensor, SensorReadListener listener) {
+
+ if (sensor == null)
+ throw new NullPointerException("\nsensor is null") ;
+
+ SensorBinding sb = getSensorBinding(sensor) ;
+ if (sb == null)
+ return ;
+
+ removeSensorReadListener(sb, listener) ;
+ if (sb.buttonBindingsList.size() == 0 &&
+ sb.readBindingsList.size() == 0)
+ removeSensorBinding(sensor) ;
+
+ listsDirty = true ;
+ }
+
+ /**
+ * Remove the given <code>SensorReadListener</code> from all sensors.
+ *
+ * @param listener the listener to remove
+ */
+ public synchronized void removeSensorReadListener
+ (SensorReadListener listener) {
+
+ Iterator i = bindingsList.iterator() ;
+ while (i.hasNext()) {
+ SensorBinding sb = (SensorBinding)i.next() ;
+ removeSensorReadListener(sb, listener) ;
+
+ if (sb.buttonBindingsList.size() == 0 &&
+ sb.readBindingsList.size() == 0) {
+ i.remove() ;
+ }
+ }
+ listsDirty = true ;
+ }
+
+ /**
+ * Remove all sensor listeners bound to the given sensor.
+ */
+ public synchronized void removeSensorBinding(Sensor sensor) {
+ Iterator i = bindingsList.iterator() ;
+ while (i.hasNext()) {
+ SensorBinding sb = (SensorBinding)i.next() ;
+ if (sb.sensor == sensor) {
+ i.remove() ;
+ break ;
+ }
+ }
+ listsDirty = true ;
+ }
+
+ /**
+ * Returns an array of references to all sensors that have been bound
+ * to listeners.
+ * @return an array of sensors, or null if no sensors have been bound
+ */
+ public Sensor[] getSensors() {
+ if (bindingsList.size() == 0)
+ return null ;
+
+ Sensor[] s = new Sensor[bindingsList.size()] ;
+ for (int i = 0 ; i < bindingsList.size() ; i++)
+ s[i] = ((SensorBinding)bindingsList.get(i)).sensor ;
+
+ return s ;
+ }
+
+ /**
+ * Copies binding lists to arrays for event dispatch. This allows
+ * listeners to add or remove themselves or other listeners safely.
+ */
+ private synchronized void updateArrays() {
+ bindings = (SensorBinding[])bindingsList.toArray
+ (new SensorBinding[bindingsList.size()]) ;
+
+ for (int i = 0 ; i < bindings.length ; i++) {
+ bindings[i].updateArrays() ;
+ }
+ }
+
+ /**
+ * Reads all sensor button state and dispatches events to registered
+ * button and read listeners. This method is intended to be called from
+ * the <code>processStimulus</code> implementation of a
+ * <code>Behavior</code> or the <code>pollAndProcessInput</code> method of
+ * an event-driven implementation of <code>InputDevice</code>.
+ */
+ public void dispatchEvents() {
+ long t1 = t0 ;
+ t0 = J3DTimer.getValue() ;
+
+ if (listsDirty) {
+ updateArrays() ;
+ listsDirty = false ;
+ }
+
+ // Loop through all sensor bindings.
+ for (int k = 0 ; k < bindings.length ; k++) {
+ SensorBinding sb = bindings[k] ;
+ Sensor s = sb.sensor ;
+ Transform3D read = sb.read ;
+ int[] buttons = sb.buttons ;
+ int dragButton = 0 ;
+ boolean callReadListeners = true ;
+ boolean callDraggedListener = false ;
+
+ // Get this sensor's readings.
+ s.getRead(read) ;
+ s.lastButtons(buttons) ;
+
+ // Dispatch button listeners.
+ for (int j = 0 ; j < sb.buttonBindings.length ; j++) {
+ SensorButtonBinding sbb = sb.buttonBindings[j] ;
+ for (int i = 0 ; i < buttons.length ; i++) {
+ if (sbb.listeners[i] == null)
+ continue ;
+
+ // Check for button release.
+ if (sbb.prevButtons[i]) {
+ if (buttons[i] == 0) {
+ e.set(source, SensorEvent.RELEASED, s, read,
+ buttons, i, t0, t1) ;
+ sbb.listeners[i].released(e) ;
+ sbb.prevButtons[i] = false ;
+ sbb.buttonsHandled-- ;
+ }
+ else {
+ callDraggedListener = true ;
+ dragButton = i ;
+ }
+ callReadListeners = false ;
+ }
+ // Check for button press.
+ // Ignore multiple button presses if not enabled;
+ // otherwise, one listener is bound to all buttons.
+ else if (buttons[i] == 1) {
+ if (sbb.buttonsHandled == 0 || sbb.multiButton) {
+ e.set(source, SensorEvent.PRESSED, s, read,
+ buttons, i, t0, t1) ;
+ sbb.listeners[i].pressed(e) ;
+ sbb.prevButtons[i] = true ;
+ sbb.buttonsHandled++ ;
+ callReadListeners = false ;
+ }
+ }
+ }
+ if (callDraggedListener) {
+ // One drag event even if multiple buttons down.
+ // Called after all pressed() and released() calls.
+ e.set(source, SensorEvent.DRAGGED, s, read, buttons,
+ SensorEvent.NOBUTTON, t0, t1) ;
+ sbb.listeners[dragButton].dragged(e) ;
+ }
+ }
+ // Dispatch read listeners.
+ if (callReadListeners) {
+ e.set(source, SensorEvent.READ, s, read,
+ buttons, SensorEvent.NOBUTTON, t0, t1) ;
+ for (int r = 0 ; r < sb.readBindings.length ; r++) {
+ sb.readBindings[r].read(e) ;
+ }
+ }
+ }
+ }
+
+ public String toString() {
+ String s = "SensorEventAgent@" + Integer.toHexString(hashCode()) ;
+ s += "\nsensor bindings:\n\n" ;
+ for (int i = 0 ; i < bindingsList.size() ; i++) {
+ s += ((SensorBinding)bindingsList.get(i)).toString() + "\n" ;
+ }
+ return s ;
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorGnomonEcho.java b/src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorGnomonEcho.java
new file mode 100644
index 0000000..8cdb576
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorGnomonEcho.java
@@ -0,0 +1,225 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.sensor ;
+
+import javax.media.j3d.Shape3D ;
+import javax.media.j3d.Material ;
+import javax.media.j3d.Appearance ;
+import javax.media.j3d.Transform3D ;
+import javax.media.j3d.GeometryArray ;
+import javax.media.j3d.TriangleArray ;
+import javax.media.j3d.TransparencyAttributes;
+import javax.vecmath.Point3f ;
+import javax.vecmath.Vector3f ;
+
+/**
+ * A Shape3D representing a gnomon pointing along each coordinate
+ * axis. The base of the gnomon is a cube, and the coordinate axes are
+ * represented by pyramids attached to each face of the cube.
+ *
+ * @since Java 3D 1.3
+ */
+public class SensorGnomonEcho extends Shape3D {
+ /**
+ * Constructs a SensorGnomonEcho. Read and write capabilities are
+ * granted for the Appearance, Material, TransparencyAttributes,
+ * and TransparencyAttributes mode and value.
+ *
+ * @param transform translation and/or rotation to apply to the gnomon
+ * geometry; this should be the position and orientation of the sensor
+ * hotspot in the sensor's local coordinate system
+ * @param baseWidth width of each edge of the base cube in meters
+ * @param axisLength distance in meters from the gnomon center to
+ * the apex of the pyramid attached to each face of the base cube
+ * @param enableLighting boolean indicating whether normals should be
+ * generated and lighting enabled
+ */
+ public SensorGnomonEcho(Transform3D transform,
+ double baseWidth,
+ double axisLength,
+ boolean enableLighting) {
+ super() ;
+
+ int FRONT = 0 ;
+ int BACK = 1 ;
+ int LEFT = 2 ;
+ int RIGHT = 3 ;
+ int TOP = 4 ;
+ int BOTTOM = 5 ;
+ Point3f[] axes = new Point3f[6] ;
+ float length = (float)axisLength ;
+
+ axes[FRONT] = new Point3f(0f, 0f, length) ;
+ axes[BACK] = new Point3f(0f, 0f, -length) ;
+ axes[LEFT] = new Point3f(-length, 0f, 0f) ;
+ axes[RIGHT] = new Point3f( length, 0f, 0f) ;
+ axes[TOP] = new Point3f(0f, length, 0f) ;
+ axes[BOTTOM] = new Point3f(0f, -length, 0f) ;
+
+ if (transform != null)
+ for (int i = FRONT ; i <= BOTTOM ; i++)
+ transform.transform(axes[i]) ;
+
+ float offset = (float)baseWidth / 2.0f ;
+ Point3f[][] cube = new Point3f[6][4] ;
+
+ cube[FRONT][0] = new Point3f(-offset, -offset, offset) ;
+ cube[FRONT][1] = new Point3f( offset, -offset, offset) ;
+ cube[FRONT][2] = new Point3f( offset, offset, offset) ;
+ cube[FRONT][3] = new Point3f(-offset, offset, offset) ;
+
+ cube[BACK][0] = new Point3f( offset, -offset, -offset) ;
+ cube[BACK][1] = new Point3f(-offset, -offset, -offset) ;
+ cube[BACK][2] = new Point3f(-offset, offset, -offset) ;
+ cube[BACK][3] = new Point3f( offset, offset, -offset) ;
+
+ if (transform != null)
+ for (int i = FRONT ; i <= BACK ; i++)
+ for (int j = 0 ; j < 4 ; j++)
+ transform.transform(cube[i][j]) ;
+
+ cube[LEFT][0] = cube[BACK][1] ;
+ cube[LEFT][1] = cube[FRONT][0] ;
+ cube[LEFT][2] = cube[FRONT][3] ;
+ cube[LEFT][3] = cube[BACK][2] ;
+
+ cube[RIGHT][0] = cube[FRONT][1] ;
+ cube[RIGHT][1] = cube[BACK][0] ;
+ cube[RIGHT][2] = cube[BACK][3] ;
+ cube[RIGHT][3] = cube[FRONT][2] ;
+
+ cube[TOP][0] = cube[FRONT][3] ;
+ cube[TOP][1] = cube[FRONT][2] ;
+ cube[TOP][2] = cube[BACK][3] ;
+ cube[TOP][3] = cube[BACK][2] ;
+
+ cube[BOTTOM][0] = cube[BACK][1] ;
+ cube[BOTTOM][1] = cube[BACK][0] ;
+ cube[BOTTOM][2] = cube[FRONT][1] ;
+ cube[BOTTOM][3] = cube[FRONT][0] ;
+
+ int v = 0 ;
+ Point3f[] vertices = new Point3f[72] ;
+
+ for (int i = 0 ; i < 6 ; i++) {
+ vertices[v++] = cube[i][0] ;
+ vertices[v++] = cube[i][1] ;
+ vertices[v++] = axes[i] ;
+ vertices[v++] = cube[i][1] ;
+ vertices[v++] = cube[i][2] ;
+ vertices[v++] = axes[i] ;
+ vertices[v++] = cube[i][2] ;
+ vertices[v++] = cube[i][3] ;
+ vertices[v++] = axes[i] ;
+ vertices[v++] = cube[i][3] ;
+ vertices[v++] = cube[i][0] ;
+ vertices[v++] = axes[i] ;
+ }
+
+ int vertexFormat ;
+ Material m = new Material() ;
+ m.setCapability(Material.ALLOW_COMPONENT_READ) ;
+ m.setCapability(Material.ALLOW_COMPONENT_WRITE) ;
+
+ if (enableLighting) {
+ vertexFormat =
+ GeometryArray.COORDINATES | GeometryArray.NORMALS ;
+ m.setLightingEnable(true) ;
+ }
+ else {
+ vertexFormat = GeometryArray.COORDINATES ;
+ m.setLightingEnable(false) ;
+ }
+
+ TriangleArray ta = new TriangleArray(72, vertexFormat) ;
+ ta.setCoordinates(0, vertices) ;
+
+ if (enableLighting) {
+ Vector3f v0 = new Vector3f() ;
+ Vector3f v1 = new Vector3f() ;
+ Vector3f[] normals = new Vector3f[72] ;
+
+ for (int i = 0 ; i < 72 ; i += 3) {
+ v0.sub(vertices[i+1], vertices[i]) ;
+ v1.sub(vertices[i+2], vertices[i]) ;
+
+ Vector3f n = new Vector3f() ;
+ n.cross(v0, v1) ;
+ n.normalize() ;
+
+ normals[i] = n ;
+ normals[i+1] = n ;
+ normals[i+2] = n ;
+ }
+ ta.setNormals(0, normals) ;
+ }
+
+ Appearance a = new Appearance() ;
+ a.setMaterial(m) ;
+ a.setCapability(Appearance.ALLOW_MATERIAL_READ) ;
+ a.setCapability(Appearance.ALLOW_MATERIAL_WRITE) ;
+
+ TransparencyAttributes tra = new TransparencyAttributes() ;
+ tra.setCapability(TransparencyAttributes.ALLOW_MODE_READ) ;
+ tra.setCapability(TransparencyAttributes.ALLOW_MODE_WRITE) ;
+ tra.setCapability(TransparencyAttributes.ALLOW_VALUE_READ) ;
+ tra.setCapability(TransparencyAttributes.ALLOW_VALUE_WRITE) ;
+ ta.setCapability
+ (TransparencyAttributes.ALLOW_BLEND_FUNCTION_READ) ;
+ ta.setCapability
+ (TransparencyAttributes.ALLOW_BLEND_FUNCTION_WRITE) ;
+
+ a.setTransparencyAttributes(tra) ;
+ a.setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_READ) ;
+ a.setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE) ;
+
+ setGeometry(ta) ;
+ setAppearance(a) ;
+
+ setCapability(ALLOW_APPEARANCE_READ) ;
+ setCapability(ALLOW_APPEARANCE_WRITE) ;
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorInputAdaptor.java b/src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorInputAdaptor.java
new file mode 100644
index 0000000..ed91bac
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorInputAdaptor.java
@@ -0,0 +1,71 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.sensor ;
+
+/**
+ * The adaptor which receives sensor button and read events. The methods
+ * in this class are empty; the ones of interest should be overridden by
+ * classes extending this adaptor.
+ *
+ * @since Java 3D 1.3
+ */
+public class SensorInputAdaptor
+ implements SensorButtonListener, SensorReadListener {
+
+ public void pressed(SensorEvent e) {
+ }
+
+ public void released(SensorEvent e) {
+ }
+
+ public void dragged(SensorEvent e) {
+ }
+
+ public void clicked(SensorEvent e) {
+ }
+
+ public void read(SensorEvent e) {
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorReadListener.java b/src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorReadListener.java
new file mode 100644
index 0000000..ceee4f7
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/sensor/SensorReadListener.java
@@ -0,0 +1,72 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.sensor ;
+
+/**
+ * This defines the interface for handling a sensor's read events in
+ * conjuction with a <code>SensorEventAgent</code> instance.
+ * <p>
+ * The events passed to this listener's methods are <i>ephemeral</i>; they
+ * are only valid until the listener has returned. If a listener needs to
+ * retain the event it must be copied using the
+ * <code>SensorEvent(SensorEvent)</code> constructor.
+ *
+ * @see SensorEvent
+ * @see SensorEventAgent
+ * @see SensorButtonListener
+ * @since Java 3D 1.3
+ */
+public interface SensorReadListener {
+ /**
+ * This method is called each time the <code>dispatchEvents</code>
+ * method of <code>SensorEventAgent</code> is called and none of a
+ * sensor's buttons have been handled by a button listener. The
+ * sensor read value has not necessarily changed since the last read
+ * event.
+ *
+ * @param e the sensor event
+ */
+ public void read(SensorEvent e) ;
+}
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/vp/OrbitBehavior.java b/src/classes/share/com/sun/j3d/utils/behaviors/vp/OrbitBehavior.java
new file mode 100644
index 0000000..9327c1f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/vp/OrbitBehavior.java
@@ -0,0 +1,1047 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.vp;
+
+import java.awt.event.ComponentEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.KeyEvent;
+import java.awt.AWTEvent;
+import java.awt.Component;
+import java.awt.Cursor;
+import javax.swing.SwingUtilities;
+
+import javax.media.j3d.WakeupOnAWTEvent;
+import javax.media.j3d.WakeupOnElapsedFrames;
+import javax.media.j3d.TransformGroup;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.View;
+import javax.media.j3d.Canvas3D;
+
+import javax.vecmath.Vector3d;
+import javax.vecmath.Point3d;
+import javax.vecmath.Matrix3d;
+
+import com.sun.j3d.utils.universe.ViewingPlatform;
+
+import com.sun.j3d.internal.J3dUtilsI18N;
+
+/**
+ * Moves the View around a point of interest when the mouse is dragged with
+ * a mouse button pressed. Includes rotation, zoom, and translation
+ * actions.
+ * <p>
+ * This behavior must be added to the ViewingPlatform
+ * using the <code>ViewingPlatform.setViewPlatformBehavior</code> method.
+ * <p>
+ * The rotate action rotates the ViewPlatform around the point of interest
+ * when the mouse is moved with the main mouse button pressed. The
+ * rotation is in the direction of the mouse movement, with a default
+ * rotation of 0.01 radians for each pixel of mouse movement.
+ * <p>
+ * The zoom action moves the ViewPlatform closer to or further from the
+ * point of interest when the mouse is moved with the middle mouse button
+ * pressed (or Alt-main mouse button on systems without a middle mouse button).
+ * The default zoom action is to translate the ViewPlatform 0.01 units for each
+ * pixel of mouse movement. Moving the mouse up moves the ViewPlatform closer,
+ * moving the mouse down moves the ViewPlatform further away.
+ * <p>
+ * By default, the zoom action allows the ViewPlatform to move through
+ * the center of rotation to orbit at a negative radius.
+ * The <code>STOP_ZOOM</code> constructor flag will stop the ViewPlatform at
+ * a minimum radius from the center. The default minimum radius is 0.0
+ * and can be set using the <code>setMinRadius</code> method.
+ * <p>
+ * The <code>PROPORTIONAL_ZOOM</code> constructor flag changes the zoom action
+ * to move the ViewPlatform proportional to its distance from the center
+ * of rotation. For this mode, the default action is to move the ViewPlatform
+ * by 1% of its distance from the center of rotation for each pixel of
+ * mouse movement.
+ * <p>
+ * The translate action translates the ViewPlatform when the mouse is moved
+ * with the right mouse button pressed (Shift-main mouse button on systems
+ * without a right mouse button). The translation is in the direction of the
+ * mouse movement, with a default translation of 0.01 units for each pixel
+ * of mouse movement.
+ * <p>
+ * The sensitivity of the actions can be scaled using the
+ * <code>set</code><i>Action</i><code>Factor()</code> methods which scale
+ * the default movement by the factor. The rotate and translate actions
+ * have separate factors for x and y.
+ * <p>
+ * The actions can be reversed using the <code>REVERSE_</code><i>ACTION</i>
+ * constructor flags. The default action moves the ViewPlatform around the
+ * objects in the scene. The <code>REVERSE_</code><i>ACTION</i> flags can
+ * make the objects in the scene appear to be moving in the direction
+ * of the mouse movement.
+ * <p>
+ * The actions can be disabled by either using the
+ * <code>DISABLE_</code><i>ACTION</i> constructor flags or the
+ * <code>set</code><i>Action</i><code>Enable</code> methods.
+ * <p>
+ * The default center of rotation is (0, 0, 0) and can be set using the
+ * <code>setRotationCenter()</code> method.
+ *
+ * @since Java 3D 1.2.1
+ */
+public class OrbitBehavior extends ViewPlatformAWTBehavior {
+
+ private Transform3D velocityTransform = new Transform3D();
+ private Transform3D longditudeTransform = new Transform3D();
+ private Transform3D rollTransform = new Transform3D();
+ private Transform3D latitudeTransform = new Transform3D();
+ private Transform3D rotateTransform = new Transform3D();
+
+ // needed for integrateTransforms but don't want to new every time
+ private Transform3D temp1 = new Transform3D();
+ private Transform3D temp2 = new Transform3D();
+ private Transform3D translation = new Transform3D();
+ private Vector3d transVector = new Vector3d();
+ private Vector3d distanceVector = new Vector3d();
+ private Vector3d centerVector = new Vector3d();
+ private Vector3d invertCenterVector = new Vector3d();
+
+ private double longditude = 0.0;
+ private double latitude = 0.0;
+ private double rollAngle = 0.0;
+ private double startDistanceFromCenter = 20.0;
+ private double distanceFromCenter = 20.0;
+ private final double MAX_MOUSE_ANGLE = Math.toRadians( 3 );
+ private final double ZOOM_FACTOR = 1.0;
+ private Point3d rotationCenter = new Point3d();
+ private Matrix3d rotMatrix = new Matrix3d();
+ private Transform3D currentXfm = new Transform3D();
+
+ private int mouseX = 0;
+ private int mouseY = 0;
+
+ private double rotXFactor = 1.0;
+ private double rotYFactor = 1.0;
+ private double transXFactor = 1.0;
+ private double transYFactor = 1.0;
+ private double zoomFactor = 1.0;
+
+ private double xtrans = 0.0;
+ private double ytrans = 0.0;
+ private double ztrans = 0.0;
+
+ private boolean zoomEnabled = true;
+ private boolean rotateEnabled = true;
+ private boolean translateEnabled = true;
+ private boolean reverseRotate = false;
+ private boolean reverseTrans = false;
+ private boolean reverseZoom = false;
+ private boolean stopZoom = false;
+ private boolean proportionalZoom = false;
+ private double minRadius = 0.0;
+ private int leftButton = ROTATE;
+ private int rightButton = TRANSLATE;
+ private int middleButton = ZOOM;
+
+ /**
+ * Constructor flag to reverse the rotate behavior
+ */
+ public static final int REVERSE_ROTATE = 0x010;
+
+ /**
+ * Constructor flag to reverse the translate behavior
+ */
+ public static final int REVERSE_TRANSLATE = 0x020;
+
+ /**
+ * Constructor flag to reverse the zoom behavior
+ */
+ public static final int REVERSE_ZOOM = 0x040;
+
+ /**
+ * Constructor flag to reverse all the behaviors
+ */
+ public static final int REVERSE_ALL = (REVERSE_ROTATE | REVERSE_TRANSLATE |
+ REVERSE_ZOOM);
+
+ /**
+ * Constructor flag that indicates zoom should stop when it reaches
+ * the minimum orbit radius set by setMinRadius(). The minimus
+ * radius default is 0.0.
+ */
+ public static final int STOP_ZOOM = 0x100;
+
+ /**
+ * Constructor flag to disable rotate
+ */
+ public static final int DISABLE_ROTATE = 0x200;
+
+ /**
+ * Constructor flag to disable translate
+ */
+ public static final int DISABLE_TRANSLATE = 0x400;
+
+ /**
+ * Constructor flag to disable zoom
+ */
+ public static final int DISABLE_ZOOM = 0x800;
+
+ /**
+ * Constructor flag to use proportional zoom, which determines
+ * how much you zoom based on view's distance from the center of
+ * rotation. The percentage of distance that the viewer zooms
+ * is determined by the zoom factor.
+ */
+ public static final int PROPORTIONAL_ZOOM = 0x1000;
+
+ /**
+ * Used to set the fuction for a mouse button to Rotate
+ */
+ private static final int ROTATE = 0;
+
+ /**
+ * Used to set the function for a mouse button to Translate
+ */
+ private static final int TRANSLATE = 1;
+
+ /**
+ * Used to set the function for a mouse button to Zoom
+ */
+ private static final int ZOOM = 2;
+
+ private static final double NOMINAL_ZOOM_FACTOR = .01;
+ private static final double NOMINAL_PZOOM_FACTOR = 1.0;
+ private static final double NOMINAL_ROT_FACTOR = .01;
+ private static final double NOMINAL_TRANS_FACTOR = .01;
+
+ private double rotXMul = NOMINAL_ROT_FACTOR * rotXFactor;
+ private double rotYMul = NOMINAL_ROT_FACTOR * rotYFactor;
+ private double transXMul = NOMINAL_TRANS_FACTOR * transXFactor;
+ private double transYMul = NOMINAL_TRANS_FACTOR * transYFactor;
+ private double zoomMul = NOMINAL_ZOOM_FACTOR * zoomFactor;
+
+ /**
+ * Parameterless constructor for this behavior. This is intended for use
+ * by ConfiguredUniverse, which requires such a constructor for
+ * configurable behaviors. The Canvas3D used to listen for mouse and
+ * mouse motion events is obtained from the superclass
+ * setViewingPlatform() method.
+ * @since Java 3D 1.3
+ */
+ public OrbitBehavior() {
+ super(MOUSE_LISTENER | MOUSE_MOTION_LISTENER);
+ }
+
+ /**
+ * Creates a new OrbitBehavior
+ *
+ * @param c The Canvas3D to add the behavior to
+ */
+ public OrbitBehavior(Canvas3D c) {
+ this(c, 0 );
+ }
+
+ /**
+ * Creates a new OrbitBehavior
+ *
+ * @param c The Canvas3D to add the behavior to
+ * @param flags The option flags
+ */
+ public OrbitBehavior(Canvas3D c, int flags) {
+ super(c, MOUSE_LISTENER | MOUSE_MOTION_LISTENER | flags );
+
+ if ((flags & DISABLE_ROTATE) != 0) rotateEnabled = false;
+ if ((flags & DISABLE_ZOOM) != 0) zoomEnabled = false;
+ if ((flags & DISABLE_TRANSLATE) != 0) translateEnabled = false;
+ if ((flags & REVERSE_TRANSLATE) != 0) reverseTrans = true;
+ if ((flags & REVERSE_ROTATE) != 0) reverseRotate = true;
+ if ((flags & REVERSE_ZOOM) != 0) reverseZoom = true;
+ if ((flags & STOP_ZOOM) != 0) stopZoom = true;
+ if ((flags & PROPORTIONAL_ZOOM) !=0) {
+ proportionalZoom = true;
+ zoomMul = NOMINAL_PZOOM_FACTOR * zoomFactor;
+ }
+ }
+
+ protected synchronized void processAWTEvents( final AWTEvent[] events ) {
+ motion = false;
+ for(int i=0; i<events.length; i++)
+ if (events[i] instanceof MouseEvent)
+ processMouseEvent( (MouseEvent)events[i] );
+ }
+
+ protected void processMouseEvent( final MouseEvent evt ) {
+
+ if (evt.getID()==MouseEvent.MOUSE_PRESSED) {
+ mouseX = evt.getX();
+ mouseY = evt.getY();
+ motion=true;
+ } else if (evt.getID()==MouseEvent.MOUSE_DRAGGED) {
+ int xchange = evt.getX() - mouseX;
+ int ychange = evt.getY() - mouseY;
+ // rotate
+ if (rotate(evt)) {
+ if (reverseRotate) {
+ longditude -= xchange * rotXMul;
+ latitude -= ychange * rotYMul;
+ }
+ else {
+ longditude += xchange * rotXMul;
+ latitude += ychange * rotYMul;
+ }
+ }
+ // translate
+ else if (translate(evt)) {
+ if (reverseTrans) {
+ xtrans -= xchange * transXMul;
+ ytrans += ychange * transYMul;
+ }
+ else {
+ xtrans += xchange * transXMul;
+ ytrans -= ychange * transYMul;
+ }
+ }
+ // zoom
+ else if (zoom(evt)) {
+ if (proportionalZoom) {
+ if (reverseZoom) {
+ if ((distanceFromCenter -
+ (zoomMul*ychange*distanceFromCenter/100.0)) >
+ minRadius) {
+ distanceFromCenter -= (zoomMul*ychange*
+ distanceFromCenter/100.0);
+ }
+ else {
+ distanceFromCenter = minRadius;
+ }
+ }
+ else {
+ if ((distanceFromCenter +
+ (zoomMul*ychange*distanceFromCenter/100.0))
+ > minRadius) {
+ distanceFromCenter += (zoomMul*ychange*
+ distanceFromCenter/100.0);
+ }
+ else {
+ distanceFromCenter = minRadius;
+ }
+ }
+ }
+ else {
+ if (stopZoom) {
+ if (reverseZoom) {
+ if ((distanceFromCenter - ychange*zoomMul) > minRadius) {
+ distanceFromCenter -= ychange*zoomMul;
+ }
+ else {
+ distanceFromCenter = minRadius;
+ }
+ }
+ else {
+ if ((distanceFromCenter + ychange*zoomMul) > minRadius) {
+ distanceFromCenter += ychange * zoomMul;
+ }
+ else {
+ distanceFromCenter = minRadius;
+ }
+ }
+ }
+ else {
+ if (reverseZoom) {
+ distanceFromCenter -= ychange*zoomMul;
+ }
+ else {
+ distanceFromCenter += ychange*zoomMul;
+ }
+ }
+ }
+ }
+ mouseX = evt.getX();
+ mouseY = evt.getY();
+ motion = true;
+ } else if (evt.getID()==MouseEvent.MOUSE_RELEASED ) {
+ }
+
+ }
+
+ /**
+ * Sets the ViewingPlatform for this behavior. This method is
+ * called by the ViewingPlatform.
+ * If a sub-calls overrides this method, it must call
+ * super.setViewingPlatform(vp).
+ * NOTE: Applications should <i>not</i> call this method.
+ */
+ public void setViewingPlatform(ViewingPlatform vp) {
+ super.setViewingPlatform( vp );
+
+ if (vp!=null) {
+ resetView();
+ integrateTransforms();
+ }
+ }
+
+ /**
+ * Reset the orientation and distance of this behavior to the current
+ * values in the ViewPlatform Transform Group
+ */
+ private void resetView() {
+ Vector3d centerToView = new Vector3d();
+
+ targetTG.getTransform( targetTransform );
+
+ targetTransform.get( rotMatrix, transVector );
+ centerToView.sub( transVector, rotationCenter );
+ distanceFromCenter = centerToView.length();
+ startDistanceFromCenter = distanceFromCenter;
+
+ targetTransform.get( rotMatrix );
+ rotateTransform.set( rotMatrix );
+
+ // compute the initial x/y/z offset
+ temp1.set(centerToView);
+ rotateTransform.invert();
+ rotateTransform.mul(temp1);
+ rotateTransform.get(centerToView);
+ xtrans = centerToView.x;
+ ytrans = centerToView.y;
+ ztrans = centerToView.z;
+
+ // reset rotMatrix
+ rotateTransform.set( rotMatrix );
+ }
+
+ protected synchronized void integrateTransforms() {
+ // Check if the transform has been changed by another
+ // behavior
+ targetTG.getTransform(currentXfm) ;
+ if (! targetTransform.equals(currentXfm))
+ resetView() ;
+
+ longditudeTransform.rotY( longditude );
+ latitudeTransform.rotX( latitude );
+ rotateTransform.mul(rotateTransform, latitudeTransform);
+ rotateTransform.mul(rotateTransform, longditudeTransform);
+
+ distanceVector.z = distanceFromCenter - startDistanceFromCenter;
+
+ temp1.set(distanceVector);
+ temp1.mul(rotateTransform, temp1);
+
+ // want to look at rotationCenter
+ transVector.x = rotationCenter.x + xtrans;
+ transVector.y = rotationCenter.y + ytrans;
+ transVector.z = rotationCenter.z + ztrans;
+
+ translation.set(transVector);
+ targetTransform.mul(temp1, translation);
+
+ // handle rotationCenter
+ temp1.set(centerVector);
+ temp1.mul(targetTransform);
+
+ invertCenterVector.x = -centerVector.x;
+ invertCenterVector.y = -centerVector.y;
+ invertCenterVector.z = -centerVector.z;
+
+ temp2.set(invertCenterVector);
+ targetTransform.mul(temp1, temp2);
+
+ targetTG.setTransform(targetTransform);
+
+ // reset yaw and pitch angles
+ longditude = 0.0;
+ latitude = 0.0;
+ }
+
+ /**
+ * Sets the center around which the View rotates.
+ * The default is (0,0,0).
+ * @param center The Point3d to set the center of rotation to
+ */
+ public synchronized void setRotationCenter(Point3d center) {
+ rotationCenter.x = center.x;
+ rotationCenter.y = center.y;
+ rotationCenter.z = center.z;
+ centerVector.set(rotationCenter);
+ }
+
+ /**
+ * Property which sets the center around which the View rotates.
+ * Used by ConfiguredUniverse.
+ * @param center array of length 1 containing an instance of Point3d
+ * @since Java 3D 1.3
+ */
+ public void RotationCenter(Object[] center) {
+ if (! (center.length == 1 && center[0] instanceof Point3d))
+ throw new IllegalArgumentException
+ ("RotationCenter must be a single Point3d");
+
+ setRotationCenter((Point3d)center[0]);
+ }
+
+ /**
+ * Places the value of the center around which the View rotates
+ * into the Point3d.
+ * @param center The Point3d
+ */
+ public void getRotationCenter(Point3d center) {
+ center.x = rotationCenter.x;
+ center.y = rotationCenter.y;
+ center.z = rotationCenter.z;
+ }
+
+ // TODO
+ // Need to add key factors for Rotate, Translate and Zoom
+ // Method calls should just update MAX_KEY_ANGLE, KEY_TRANSLATE and
+ // KEY_ZOOM
+ //
+ // Methods also need to correctly set sign of variables depending on
+ // the Reverse settings.
+
+ /**
+ * Sets the rotation x and y factors. The factors are used to determine
+ * how many radians to rotate the view for each pixel of mouse movement.
+ * The view is rotated factor * 0.01 radians for each pixel of mouse
+ * movement. The default factor is 1.0.
+ * @param xfactor The x movement multiplier
+ * @param yfactor The y movement multiplier
+ **/
+ public synchronized void setRotFactors(double xfactor, double yfactor) {
+ rotXFactor = xfactor;
+ rotYFactor = yfactor;
+ rotXMul = NOMINAL_ROT_FACTOR * xfactor;
+ rotYMul = NOMINAL_ROT_FACTOR * yfactor;
+ }
+
+ /**
+ * Property which sets the rotation x and y factors.
+ * Used by ConfiguredUniverse.
+ * @param factors array of length 2 containing instances of Double
+ * @since Java 3D 1.3
+ */
+ public void RotFactors(Object[] factors) {
+ if (! (factors.length == 2 &&
+ factors[0] instanceof Double && factors[1] instanceof Double))
+ throw new IllegalArgumentException
+ ("RotFactors must be two Doubles");
+
+ setRotFactors(((Double)factors[0]).doubleValue(),
+ ((Double)factors[1]).doubleValue());
+ }
+
+ /**
+ * Sets the rotation x factor. The factors are used to determine
+ * how many radians to rotate the view for each pixel of mouse movement.
+ * The view is rotated factor * 0.01 radians for each pixel of mouse
+ * movement. The default factor is 1.0.
+ * @param xfactor The x movement multiplier
+ **/
+ public synchronized void setRotXFactor(double xfactor) {
+ rotXFactor = xfactor;
+ rotXMul = NOMINAL_ROT_FACTOR * xfactor;
+ }
+
+ /**
+ * Property which sets the rotation x factor.
+ * Used by ConfiguredUniverse.
+ * @param xFactor array of length 1 containing instance of Double
+ * @since Java 3D 1.3
+ */
+ public void RotXFactor(Object[] xFactor) {
+ if (! (xFactor.length == 1 && xFactor[0] instanceof Double))
+ throw new IllegalArgumentException("RotXFactor must be a Double");
+
+ setRotXFactor(((Double)xFactor[0]).doubleValue());
+ }
+
+ /**
+ * Sets the rotation y factor. The factors are used to determine
+ * how many radians to rotate the view for each pixel of mouse movement.
+ * The view is rotated factor * 0.01 radians for each pixel of mouse
+ * movement. The default factor is 1.0.
+ * @param yfactor The y movement multiplier
+ **/
+ public synchronized void setRotYFactor(double yfactor) {
+ rotYFactor = yfactor;
+ rotYMul = NOMINAL_ROT_FACTOR * yfactor;
+ }
+
+ /**
+ * Property which sets the rotation y factor.
+ * Used by ConfiguredUniverse.
+ * @param yFactor array of length 1 containing instance of Double
+ * @since Java 3D 1.3
+ */
+ public void RotYFactor(Object[] yFactor) {
+ if (! (yFactor.length == 1 && yFactor[0] instanceof Double))
+ throw new IllegalArgumentException("RotYFactor must be a Double");
+
+ setRotYFactor(((Double)yFactor[0]).doubleValue());
+ }
+
+ /**
+ * Sets the translation x and y factors. The factors are used to determine
+ * how many units to translate the view for each pixel of mouse movement.
+ * The view is translated factor * 0.01 units for each pixel of mouse
+ * movement. The default factor is 1.0.
+ * @param xfactor The x movement multiplier
+ * @param yfactor The y movement multiplier
+ **/
+ public synchronized void setTransFactors(double xfactor,
+ double yfactor) {
+ transXFactor = xfactor;
+ transYFactor = yfactor;
+ transXMul = NOMINAL_TRANS_FACTOR * xfactor;
+ transYMul = NOMINAL_TRANS_FACTOR * yfactor;
+ }
+
+ /**
+ * Property which sets the translation x and y factors.
+ * Used by ConfiguredUniverse.
+ * @param factors array of length 2 containing instances of Double
+ * @since Java 3D 1.3
+ */
+ public void TransFactors(Object[] factors) {
+ if (! (factors.length == 2 &&
+ factors[0] instanceof Double && factors[1] instanceof Double))
+ throw new IllegalArgumentException
+ ("TransFactors must be two Doubles");
+
+ setTransFactors(((Double)factors[0]).doubleValue(),
+ ((Double)factors[1]).doubleValue());
+ }
+
+ /**
+ * Sets the translation x factor. The factors are used to determine
+ * how many units to translate the view for each pixel of mouse movement.
+ * The view is translated factor * 0.01 units for each pixel of mouse
+ * movement. The default factor is 1.0.
+ * @param xfactor The x movement multiplier
+ **/
+ public synchronized void setTransXFactor(double xfactor) {
+ transXFactor = xfactor;
+ transXMul = NOMINAL_TRANS_FACTOR * xfactor;
+ }
+
+ /**
+ * Property which sets the translation x factor.
+ * Used by ConfiguredUniverse.
+ * @param xFactor array of length 1 containing instance of Double
+ * @since Java 3D 1.3
+ */
+ public void TransXFactor(Object[] xFactor) {
+ if (! (xFactor.length == 1 && xFactor[0] instanceof Double))
+ throw new IllegalArgumentException("TransXFactor must be a Double");
+
+ setTransXFactor(((Double)xFactor[0]).doubleValue());
+ }
+
+ /**
+ * Sets the translation y factor. The factors are used to determine
+ * how many units to translate the view for each pixel of mouse movement.
+ * The view is translated factor * 0.01 units for each pixel of mouse
+ * movement. The default factor is 1.0.
+ * @param yfactor The y movement multiplier
+ **/
+ public synchronized void setTransYFactor(double yfactor) {
+ transYFactor = yfactor;
+ transYMul = NOMINAL_TRANS_FACTOR * yfactor;
+ }
+
+ /**
+ * Property which sets the translation y factor.
+ * Used by ConfiguredUniverse.
+ * @param yFactor array of length 1 containing instance of Double
+ * @since Java 3D 1.3
+ */
+ public void TransYFactor(Object[] yFactor) {
+ if (! (yFactor.length == 1 && yFactor[0] instanceof Double))
+ throw new IllegalArgumentException("TransYFactor must be a Double");
+
+ setTransYFactor(((Double)yFactor[0]).doubleValue());
+ }
+
+ /**
+ * Sets the zoom factor. The factor is used to determine how many
+ * units to zoom the view for each pixel of mouse movement.
+ * The view is zoomed factor * 0.01 units for each pixel of mouse
+ * movement. For proportional zoom, the view is zoomed factor * 1%
+ * of the distance from the center of rotation for each pixel of
+ * mouse movement. The default factor is 1.0.
+ * @param zfactor The movement multiplier
+ */
+ public synchronized void setZoomFactor(double zfactor) {
+ zoomFactor = zfactor;
+ if (proportionalZoom) {
+ zoomMul = NOMINAL_PZOOM_FACTOR * zfactor;
+ }
+ else {
+ zoomMul = NOMINAL_ZOOM_FACTOR * zfactor;
+ }
+ }
+
+ /**
+ * Property which sets the zoom factor.
+ * Used by ConfiguredUniverse.
+ * @param zFactor array of length 1 containing instance of Double
+ * @since Java 3D 1.3
+ */
+ public void ZoomFactor(Object[] zFactor) {
+ if (! (zFactor.length == 1 && zFactor[0] instanceof Double))
+ throw new IllegalArgumentException("ZoomFactor must be a Double");
+
+ setZoomFactor(((Double)zFactor[0]).doubleValue());
+ }
+
+ /**
+ * Returns the x rotation movement multiplier
+ * @return The movement multiplier for x rotation
+ */
+ public double getRotXFactor() {
+ return rotXFactor;
+ }
+
+ /**
+ * Returns the y rotation movement multiplier
+ * @return The movement multiplier for y rotation
+ */
+ public double getRotYFactor() {
+ return rotYFactor;
+ }
+
+ /**
+ * Returns the x translation movement multiplier
+ * @return The movement multiplier for x translation
+ */
+ public double getTransXFactor() {
+ return transXFactor;
+ }
+
+ /**
+ * Returns the y translation movement multiplier
+ * @return The movement multiplier for y translation
+ */
+ public double getTransYFactor() {
+ return transYFactor;
+ }
+
+ /**
+ * Returns the zoom movement multiplier
+ * @return The movement multiplier for zoom
+ */
+ public double getZoomFactor() {
+ return zoomFactor;
+ }
+
+ /**
+ * Enables or disables rotation. The default is true.
+ * @param enabled true or false to enable or disable rotate
+ */
+ public synchronized void setRotateEnable(boolean enabled) {
+ rotateEnabled = enabled;
+ }
+
+ /**
+ * Property which enables or disables rotation.
+ * Used by ConfiguredUniverse.
+ * @param enabled array of length 1 containing instance of Boolean
+ * @since Java 3D 1.3
+ */
+ public void RotateEnable(Object[] enabled) {
+ if (! (enabled.length == 1 && enabled[0] instanceof Boolean))
+ throw new IllegalArgumentException("RotateEnable must be Boolean");
+
+ setRotateEnable(((Boolean)enabled[0]).booleanValue());
+ }
+
+ /**
+ * Enables or disables zoom. The default is true.
+ * @param enabled true or false to enable or disable zoom
+ */
+ public synchronized void setZoomEnable(boolean enabled) {
+ zoomEnabled = enabled;
+ }
+
+ /**
+ * Property which enables or disables zoom.
+ * Used by ConfiguredUniverse.
+ * @param enabled array of length 1 containing instance of Boolean
+ * @since Java 3D 1.3
+ */
+ public void ZoomEnable(Object[] enabled) {
+ if (! (enabled.length == 1 && enabled[0] instanceof Boolean))
+ throw new IllegalArgumentException("ZoomEnable must be Boolean");
+
+ setZoomEnable(((Boolean)enabled[0]).booleanValue());
+ }
+
+ /**
+ * Enables or disables translate. The default is true.
+ * @param enabled true or false to enable or disable translate
+ */
+ public synchronized void setTranslateEnable(boolean enabled) {
+ translateEnabled = enabled;
+ }
+
+ /**
+ * Property which enables or disables translate.
+ * Used by ConfiguredUniverse.
+ * @param enabled array of length 1 containing instance of Boolean
+ * @since Java 3D 1.3
+ */
+ public void TranslateEnable(Object[] enabled) {
+ if (! (enabled.length == 1 && enabled[0] instanceof Boolean))
+ throw new IllegalArgumentException
+ ("TranslateEnable must be Boolean");
+
+ setTranslateEnable(((Boolean)enabled[0]).booleanValue());
+ }
+
+ /**
+ * Retrieves the state of rotate enabled
+ * @return the rotate enable state
+ */
+ public boolean getRotateEnable() {
+ return rotateEnabled;
+ }
+
+ /**
+ * Retrieves the state of zoom enabled
+ * @return the zoom enable state
+ */
+ public boolean getZoomEnable() {
+ return zoomEnabled;
+ }
+
+ /**
+ * Retrieves the state of translate enabled
+ * @return the translate enable state
+ */
+ public boolean getTranslateEnable() {
+ return translateEnabled;
+ }
+
+ boolean rotate(MouseEvent evt) {
+ if (rotateEnabled) {
+ if ((leftButton == ROTATE) &&
+ (!evt.isAltDown() && !evt.isMetaDown())) {
+ return true;
+ }
+ if ((middleButton == ROTATE) &&
+ (evt.isAltDown() && !evt.isMetaDown())) {
+ return true;
+ }
+ if ((rightButton == ROTATE) &&
+ (!evt.isAltDown() && evt.isMetaDown())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean zoom(MouseEvent evt) {
+ if (zoomEnabled) {
+ if ((leftButton == ZOOM) &&
+ (!evt.isAltDown() && !evt.isMetaDown())) {
+ return true;
+ }
+ if ((middleButton == ZOOM) &&
+ (evt.isAltDown() && !evt.isMetaDown())) {
+ return true;
+ }
+ if ((rightButton == ZOOM) &&
+ (!evt.isAltDown() && evt.isMetaDown())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean translate(MouseEvent evt) {
+ if (translateEnabled) {
+ if ((leftButton == TRANSLATE) &&
+ (!evt.isAltDown() && !evt.isMetaDown())) {
+ return true;
+ }
+ if ((middleButton == TRANSLATE) &&
+ (evt.isAltDown() && !evt.isMetaDown())) {
+ return true;
+ }
+ if ((rightButton == TRANSLATE) &&
+ (!evt.isAltDown() && evt.isMetaDown())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Sets the minimum radius for the OrbitBehavior. The zoom will
+ * stop at this distance from the center of rotation. The default
+ * is 0.0. The minimum will have no affect if the STOP_ZOOM constructor
+ * flag is not set.
+ * @param r the minimum radius
+ * @exception IllegalArgumentException if the radius is less than 0.0
+ */
+ public synchronized void setMinRadius(double r) {
+ if (r < 0.0) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("OrbitBehavior1"));
+ }
+ minRadius = r;
+ }
+
+ /**
+ * Property which sets the minimum radius for the OrbitBehavior.
+ * Used by ConfiguredUniverse.
+ * @param r array of length 1 containing instance of Double
+ * @since Java 3D 1.3
+ */
+ public void MinRadius(Object[] r) {
+ if (! (r.length == 1 && r[0] instanceof Double))
+ throw new IllegalArgumentException("MinRadius must be a Double");
+
+ setMinRadius(((Double)r[0]).doubleValue());
+ }
+
+ /**
+ * Returns the minimum orbit radius. The zoom will stop at this distance
+ * from the center of rotation if the STOP_ZOOM constructor flag is set.
+ * @return the minimum radius
+ */
+ public double getMinRadius() {
+ return minRadius;
+ }
+
+ /**
+ * Set reverse translate behavior. The default is false.
+ * @param state if true, reverse translate behavior
+ * @since Java 3D 1.3
+ */
+ public void setReverseTranslate(boolean state) {
+ reverseTrans = state;
+ }
+
+ /**
+ * Property which sets reverse translate behavior.
+ * Used by ConfiguredUniverse.
+ * @param state array of length 1 containing instance of Boolean
+ * @since Java 3D 1.3
+ */
+ public void ReverseTranslate(Object[] state) {
+ if (! (state.length == 1 && state[0] instanceof Boolean))
+ throw new IllegalArgumentException
+ ("ReverseTranslate must be Boolean");
+
+ setReverseTranslate(((Boolean)state[0]).booleanValue());
+ }
+
+ /**
+ * Set reverse rotate behavior. The default is false.
+ * @param state if true, reverse rotate behavior
+ * @since Java 3D 1.3
+ */
+ public void setReverseRotate(boolean state) {
+ reverseRotate = state;
+ }
+
+ /**
+ * Property which sets reverse rotate behavior.
+ * Used by ConfiguredUniverse.
+ * @param state array of length 1 containing instance of Boolean
+ * @since Java 3D 1.3
+ */
+ public void ReverseRotate(Object[] state) {
+ if (! (state.length == 1 && state[0] instanceof Boolean))
+ throw new IllegalArgumentException("ReverseRotate must be Boolean");
+
+ setReverseRotate(((Boolean)state[0]).booleanValue());
+ }
+
+ /**
+ * Set reverse zoom behavior. The default is false.
+ * @param state if true, reverse zoom behavior
+ * @since Java 3D 1.3
+ */
+ public void setReverseZoom(boolean state) {
+ reverseZoom = state;
+ }
+
+ /**
+ * Property which sets reverse zoom behavior.
+ * Used by ConfiguredUniverse.
+ * @param state array of length 1 containing instance of Boolean
+ * @since Java 3D 1.3
+ */
+ public void ReverseZoom(Object[] state) {
+ if (! (state.length == 1 && state[0] instanceof Boolean))
+ throw new IllegalArgumentException("ReverseZoom must be Boolean");
+
+ setReverseZoom(((Boolean)state[0]).booleanValue());
+ }
+
+ /**
+ * Set proportional zoom behavior. The default is false.
+ * @param state if true, use proportional zoom behavior
+ * @since Java 3D 1.3
+ */
+ public synchronized void setProportionalZoom(boolean state) {
+ proportionalZoom = state;
+
+ if (state) {
+ zoomMul = NOMINAL_PZOOM_FACTOR * zoomFactor;
+ }
+ else {
+ zoomMul = NOMINAL_ZOOM_FACTOR * zoomFactor;
+ }
+ }
+
+ /**
+ * Property which sets proportional zoom behavior.
+ * Used by ConfiguredUniverse.
+ * @param state array of length 1 containing instance of Boolean
+ * @since Java 3D 1.3
+ */
+ public void ProportionalZoom(Object[] state) {
+ if (! (state.length == 1 && state[0] instanceof Boolean))
+ throw new IllegalArgumentException
+ ("ProportionalZoom must be Boolean");
+
+ setProportionalZoom(((Boolean)state[0]).booleanValue());
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/vp/ViewPlatformAWTBehavior.java b/src/classes/share/com/sun/j3d/utils/behaviors/vp/ViewPlatformAWTBehavior.java
new file mode 100644
index 0000000..18ce5b8
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/vp/ViewPlatformAWTBehavior.java
@@ -0,0 +1,400 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.vp;
+
+import java.awt.event.ComponentEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.event.KeyListener;
+import java.awt.event.KeyEvent;
+import java.awt.AWTEvent;
+import java.awt.Component;
+import java.awt.Cursor;
+import javax.swing.SwingUtilities;
+import java.util.ArrayList;
+
+import javax.media.j3d.WakeupOnBehaviorPost;
+import javax.media.j3d.WakeupOnElapsedFrames;
+import javax.media.j3d.WakeupOr;
+import javax.media.j3d.WakeupCriterion;
+import javax.media.j3d.WakeupCondition;
+import javax.media.j3d.TransformGroup;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.View;
+import javax.media.j3d.Canvas3D;
+
+import javax.vecmath.Vector3f;
+import com.sun.j3d.utils.universe.*;
+
+
+/**
+ * Abstract class which implements much of the event tracking and
+ * state updating in a thread safe manner.
+ *
+ * AWT Events are captured and placed in a queue.
+ *
+ * While there are pending events or motion the behavior will wake
+ * up every frame, call processAWTEvents and integrateTransforms.
+ *
+ * @since Java 3D 1.2.1
+ */
+public abstract class ViewPlatformAWTBehavior extends ViewPlatformBehavior
+implements MouseListener, MouseMotionListener, KeyListener {
+
+ private final static boolean DEBUG = false;
+
+ /**
+ * Behavior PostId used in this behavior
+ */
+ protected final static int POST_ID = 9998;
+
+ /**
+ * The different criterion for the behavior to wakeup
+ */
+ protected WakeupOnElapsedFrames frameWakeup;
+
+ /**
+ * The Or of the different criterion for the behavior to wakeup
+ */
+ protected WakeupOnBehaviorPost postWakeup;
+
+ /**
+ * The target Transform3D for this behavior
+ */
+ protected Transform3D targetTransform = new Transform3D();
+
+ /**
+ * Boolean for whether the mouse is in motion
+ */
+ protected boolean motion = false;
+
+ /**
+ * Flag indicating Behavior should listen for Mouse Events
+ */
+ public final static int MOUSE_LISTENER = 0x01;
+
+ /**
+ * Flag indicating Behavior should listen for Mouse Motion Events
+ */
+ public final static int MOUSE_MOTION_LISTENER = 0x02;
+
+ /**
+ * Flag indicating Behavior should listen for Key Events
+ */
+ public final static int KEY_LISTENER = 0x04;
+
+ /**
+ * The Canvas3Ds from which this Behavior gets AWT events
+ */
+ protected Canvas3D canvases[];
+
+ private ArrayList eventQueue = new ArrayList();
+ private int listenerFlags = 0;
+ private boolean firstEvent = false;
+
+ /**
+ * Parameterless constructor for this behavior, intended for use by
+ * subclasses instantiated through ConfiguredUniverse. Such a constructor
+ * is required for configurable behaviors.
+ * @since Java 3D 1.3
+ */
+ protected ViewPlatformAWTBehavior() {
+ super();
+ }
+
+ /**
+ * Construct a behavior which listens for events specified by the given
+ * flags, intended for use by subclasses instantiated through
+ * ConfiguredUniverse.
+ *
+ * @param listenerFlags Indicates which listener should be registered,
+ * one or more of MOUSE_LISTENER, MOUSE_MOTION_LISTENER, KEY_LISTENER
+ * @since Java 3D 1.3
+ */
+ protected ViewPlatformAWTBehavior(int listenerFlags) {
+ super();
+ setListenerFlags(listenerFlags);
+ }
+
+ /**
+ * Constructs a new ViewPlatformAWTBehavior.
+ *
+ * @param c The Canvas3D on which to listen for events. If this is null a
+ * NullPointerException will be thrown.
+ * @param listenerFlags Indicates which listener should be registered,
+ * one or more of MOUSE_LISTENER, MOUSE_MOTION_LISTENER, KEY_LISTENER
+ */
+ public ViewPlatformAWTBehavior(Canvas3D c, int listenerFlags ) {
+ super();
+
+ if (c == null)
+ throw new NullPointerException();
+
+ canvases = new Canvas3D[] { c };
+ setListenerFlags(listenerFlags);
+ }
+
+ /**
+ * Sets listener flags for this behavior.
+ *
+ * @param listenerFlags Indicates which listener should be registered,
+ * one or more of MOUSE_LISTENER, MOUSE_MOTION_LISTENER, KEY_LISTENER
+ * @since Java 3D 1.3
+ */
+ protected void setListenerFlags(int listenerFlags) {
+ this.listenerFlags = listenerFlags;
+ }
+
+ /**
+ * Initializes the behavior.
+ * NOTE: Applications should not call this method. It is called by the
+ * Java 3D behavior scheduler.
+ */
+ public void initialize() {
+ frameWakeup = new WakeupOnElapsedFrames( 0 );
+ postWakeup = new WakeupOnBehaviorPost( this, POST_ID );
+
+ wakeupOn(postWakeup);
+ }
+
+ /**
+ * Process a stimulus meant for this behavior.
+ * NOTE: Applications should not call this method. It is called by the
+ * Java 3D behavior scheduler.
+ */
+ public void processStimulus( java.util.Enumeration behEnum ) {
+ boolean hadPost = false;
+
+ while(behEnum.hasMoreElements()) {
+ WakeupCondition wakeup = (WakeupCondition)behEnum.nextElement();
+ if (wakeup instanceof WakeupOnBehaviorPost) {
+ hadPost = true;
+ } else if (wakeup instanceof WakeupOnElapsedFrames) {
+ AWTEvent[] events = null;
+ // access to event queue must be synchronized
+ synchronized(eventQueue) {
+ events = (AWTEvent[])eventQueue.toArray( new AWTEvent[eventQueue.size()] );
+ eventQueue.clear();
+ }
+ processAWTEvents(events);
+
+ if (motion)
+ integrateTransforms();
+ }
+ }
+
+ if (motion || hadPost) {
+ // wake up on behavior posts and elapsed frames if in motion
+ wakeupOn( frameWakeup );
+ } else {
+ // only wake up on behavior posts if not in motion
+ wakeupOn( postWakeup );
+ }
+ }
+
+ /**
+ * Overload setEnable from Behavior.
+ *
+ * Adds/Removes the AWT listeners depending on the requested
+ * state.
+ */
+ public void setEnable( boolean state ) {
+ if (state==getEnable())
+ return;
+
+ super.setEnable(state);
+
+ if (canvases != null) {
+ enableListeners(state);
+ }
+ }
+
+ private void enableListeners( boolean enable ) {
+ if (enable) {
+ firstEvent = true ;
+ if ( (listenerFlags & MOUSE_LISTENER)!=0)
+ for(int i=0; i<canvases.length; i++)
+ canvases[i].addMouseListener(this);
+
+ if ( (listenerFlags & MOUSE_MOTION_LISTENER)!=0)
+ for(int i=0; i<canvases.length; i++)
+ canvases[i].addMouseMotionListener(this);
+
+ if ( (listenerFlags & KEY_LISTENER)!=0)
+ for(int i=0; i<canvases.length; i++)
+ canvases[i].addKeyListener(this);
+ } else {
+ if ( (listenerFlags & MOUSE_LISTENER)!=0)
+ for(int i=0; i<canvases.length; i++)
+ canvases[i].removeMouseListener(this);
+
+ if ( (listenerFlags & MOUSE_MOTION_LISTENER)!=0)
+ for(int i=0; i<canvases.length; i++)
+ canvases[i].removeMouseMotionListener(this);
+
+ if ( (listenerFlags & KEY_LISTENER)!=0)
+ for(int i=0; i<canvases.length; i++)
+ canvases[i].removeKeyListener(this);
+ }
+ }
+
+ /**
+ * Sets the ViewingPlatform for this behavior. This method is
+ * called by the ViewingPlatform.
+ * If a sub-calls overrides this method, it must call
+ * super.setViewingPlatform(vp).
+ * NOTE: Applications should <i>not</i> call this method.
+ */
+ public void setViewingPlatform(ViewingPlatform vp) {
+ super.setViewingPlatform( vp );
+
+ if (vp==null) {
+ enableListeners( false );
+ } else {
+ if (canvases != null) {
+ // May be switching canvases here.
+ enableListeners(false);
+ }
+
+ // Use the canvases associated with viewer[0]; if no viewer is
+ // available yet, see if a canvas was provided with a constructor.
+ Viewer[] viewers = vp.getViewers();
+
+ if (viewers != null && viewers[0] != null)
+ canvases = viewers[0].getCanvas3Ds();
+
+ if (canvases == null || canvases[0] == null)
+ throw new IllegalStateException("No canvases available");
+
+ if (getEnable()) {
+ enableListeners(true);
+ }
+ }
+ }
+
+ /**
+ * This is called once per frame if there are any AWT events to
+ * process.
+ *
+ * The <code>motion</code> variable will be true when the method
+ * is called. If it is true when the method returns integrateTransforms
+ * will be called immediately.
+ *
+ * The AWTEvents are presented in the array in the order in which they
+ * arrived from AWT.
+ */
+ protected abstract void processAWTEvents( final java.awt.AWTEvent[] events );
+
+ /**
+ * Called once per frame (if the view is moving) to calculate the new
+ * view platform transform
+ */
+ protected abstract void integrateTransforms();
+
+ /**
+ * Queue AWTEvents in a thread safe manner.
+ *
+ * If subclasses override this method they must call
+ * super.queueAWTEvent(e)
+ */
+ protected void queueAWTEvent( AWTEvent e ) {
+ // add new event to the queue
+ // must be MT safe
+ synchronized (eventQueue) {
+ eventQueue.add(e);
+ // Only need to post if this is the only event in the queue.
+ // There have been reports that the first event after
+ // setViewingPlatform() is sometimes missed, so check the
+ // firstEvent flag as well.
+ if (firstEvent || eventQueue.size() == 1) {
+ firstEvent = false;
+ postId( POST_ID );
+ }
+ }
+ }
+
+ public void mouseClicked(final MouseEvent e) {
+ queueAWTEvent( e );
+ }
+
+ public void mouseEntered(final MouseEvent e) {
+ queueAWTEvent( e );
+ }
+
+ public void mouseExited(final MouseEvent e) {
+ queueAWTEvent( e );
+ }
+
+ public void mousePressed(final MouseEvent e) {
+ queueAWTEvent( e );
+ }
+
+ public void mouseReleased(final MouseEvent e) {
+ queueAWTEvent( e );
+ }
+
+ public void mouseDragged(final MouseEvent e) {
+ queueAWTEvent( e );
+ }
+
+ public void mouseMoved(final MouseEvent e) {
+ queueAWTEvent( e );
+ }
+
+ public void keyReleased(final java.awt.event.KeyEvent e) {
+ queueAWTEvent( e );
+ }
+
+ public void keyPressed(final java.awt.event.KeyEvent e) {
+ queueAWTEvent( e );
+ }
+
+ public void keyTyped(final java.awt.event.KeyEvent e) {
+ queueAWTEvent( e );
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/vp/ViewPlatformBehavior.java b/src/classes/share/com/sun/j3d/utils/behaviors/vp/ViewPlatformBehavior.java
new file mode 100644
index 0000000..a0d4d8a
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/vp/ViewPlatformBehavior.java
@@ -0,0 +1,137 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.vp;
+
+import javax.vecmath.*;
+import javax.media.j3d.*;
+import com.sun.j3d.utils.universe.*;
+
+/**
+ * Abstract class for ViewPlatformBehaviors. A ViewPlatformBehavior must
+ * be added to the ViewingPlatform with the
+ * ViewingPlatform.addViewPlatformBehavior() method. The ViewPlatformBehavior
+ * will operate on the ViewPlatform transform (the TransformGroup return by
+ * ViewingPlatform.getViewPlatformTransform()).
+ * @since Java 3D 1.2.1
+ */
+abstract public class ViewPlatformBehavior extends Behavior {
+
+ /**
+ * The ViewingPlatform for this behavior.
+ */
+ protected ViewingPlatform vp;
+
+ /**
+ * The target TransformGroup for this behavior.
+ */
+ protected TransformGroup targetTG;
+
+ /**
+ * The "home" transform for this behavior. This is a transform used to
+ * position and orient the ViewingPlatform to a known point of interest.
+ *
+ * @since Java 3D 1.3
+ */
+ protected Transform3D homeTransform = null;
+
+ /**
+ * Sets the ViewingPlatform for this behavior. This method is called by
+ * the ViewingPlatform. If a sub-calls overrides this method, it must
+ * call super.setViewingPlatform(vp).<p>
+ *
+ * NOTE: Applications should <i>not</i> call this method.
+ *
+ * @param vp the target ViewingPlatform for this behavior
+ */
+ public void setViewingPlatform(ViewingPlatform vp) {
+ this.vp = vp;
+
+ if (vp!=null)
+ targetTG = vp.getViewPlatformTransform();
+ else
+ targetTG = null;
+ }
+
+ /**
+ * Returns the ViewingPlatform for this behavior
+ * @return the ViewingPlatform for this behavior
+ */
+ public ViewingPlatform getViewingPlatform() {
+ return vp;
+ }
+
+ /**
+ * Copies the given Transform3D into the "home" transform, used to
+ * position and reorient the ViewingPlatform to a known point of interest.
+ *
+ * @param home source transform to be copied
+ * @since Java 3D 1.3
+ */
+ public void setHomeTransform(Transform3D home) {
+ if (homeTransform == null)
+ homeTransform = new Transform3D(home);
+ else
+ homeTransform.set(home);
+ }
+
+ /**
+ * Returns the behaviors "home" transform.
+ *
+ * @param home transform to be returned
+ * @since Java 3D 1.3
+ */
+ public void getHomeTransform(Transform3D home ) {
+ home.set( homeTransform );
+ }
+
+ /**
+ * Positions and reorients the ViewingPlatform to its "home" transform.
+ * @since Java 3D 1.3
+ */
+ public void goHome() {
+ if (targetTG != null && homeTransform != null)
+ targetTG.setTransform(homeTransform);
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/behaviors/vp/WandViewBehavior.java b/src/classes/share/com/sun/j3d/utils/behaviors/vp/WandViewBehavior.java
new file mode 100644
index 0000000..9f46650
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/behaviors/vp/WandViewBehavior.java
@@ -0,0 +1,3843 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.behaviors.vp ;
+
+import java.util.* ;
+import javax.vecmath.* ;
+import javax.media.j3d.* ;
+import com.sun.j3d.utils.universe.* ;
+import com.sun.j3d.utils.behaviors.sensor.* ;
+
+/**
+ * Manipulates view platform transforms using a motion-tracked wand or mouse
+ * equipped with a six degree of freedom (6DOF) sensor. An optional two axis
+ * (2D) valuator sensor is also directly supported. Default operation is set
+ * up to enable both direct manipulation of the view transform and translation
+ * back and forth along the direction the 6DOF sensor is pointing; rotation
+ * is handled by the 2D valuator if available. An arbitrary number of sensors
+ * and action bindings can be customized by accessing this behavior's
+ * <code>SensorEventAgent</code> directly.
+ * <p>
+ * This behavior can be instantiated from the configuration file read by
+ * <code>ConfiguredUniverse</code> and fully configured using the
+ * <code>ViewPlatformBehaviorProperties</code> command to set the properties
+ * described below, but neither <code>ConfiguredUniverse</code> nor
+ * <code>SimpleUniverse</code> are required by this behavior. Conventional
+ * <code>set</code> and <code>get</code> accessors are provided for
+ * configuring this behavior directly; these methods have the same names as
+ * the properties but are prefixed with <code>get</code> and <code>set</code>.
+ * Property values are spelled with mixed case strings while the corresponding
+ * constant field names for the conventional accessors are spelled with upper
+ * case strings and underscores.
+ * <p>
+ * {@link #Sensor6D Sensor6D} is the 6DOF sensor to use. This can also be set
+ * directly with the appropriate constructor. This sensor must generate 6
+ * degree of freedom position and orientation reads relative to the tracker
+ * base in physical units. By default this behavior provides an echo for the
+ * 6DOF sensor which indicates its position and orientation in the virtual
+ * world; the echo attributes can be set by the {@link #EchoType EchoType},
+ * {@link #EchoSize EchoSize}, {@link #EchoColor EchoColor}, and {@link
+ * #EchoTransparency EchoTransparency} properties. See also the {@link
+ * #NominalSensorRotation NominalSensorRotation} property, and the
+ * <code>setHotSpot</code> method of the <code>Sensor</code> class.
+ * <p>
+ * {@link #Sensor2D Sensor2D} is an optional 2D valuator to use in conjunction
+ * with the 6DOF sensor. This can be set directly with the appropriate
+ * constructor. The valuator should generate X and Y reads ranging from [-1.0
+ * .. +1.0], with a nominal (deadzone) value of 0.0. The default
+ * configuration expects to find these values along the translation components
+ * of the read matrix, at indices 3 and 7, but these indices can be also be
+ * specified by the {@link #MatrixIndices2D MatrixIndices2D} property.
+ * <p>
+ * {@link #ButtonAction6D ButtonAction6D} sets an action for a specific button
+ * on a 6DOF sensor. The actions available are:
+ * <ul>
+ * <li>
+ * <code>GrabView</code> - Directly manipulates the view platform by moving
+ * it in inverse response to the sensor's position and orientation,
+ * producing the effect of attaching the virtual world to the sensor's
+ * movements. If a button is available then this action is bound to button 0
+ * by default.
+ * </li>
+ * <li>
+ * <code>TranslateForward</code> - Translates the view platform forward along
+ * the direction the sensor is pointing; the virtual world appears to move
+ * towards the sensor. The default is button 1 if two buttons are available.
+ * Related properties are {@link #TranslationSpeed TranslationSpeed}, {@link
+ * #AccelerationTime AccelerationTime}, {@link #ConstantSpeedTime
+ * ConstantSpeedTime}, and {@link #FastSpeedFactor FastSpeedFactor}.
+ * </li>
+ * <li>
+ * <code>TranslateBackward</code> - Translates the view platform backwards
+ * along the direction the sensor is pointing; the virtual world appears to
+ * move away from the sensor. The default is button 2 if three buttons are
+ * available.
+ * </li>
+ * <li>
+ * <code>RotateCCW</code> - Rotates the view platform counter-clockwise about
+ * a Y axis; the virtual world appears to rotate clockwise. This action is
+ * not assigned by default. Related properties are {@link #RotationSpeed
+ * RotationSpeed}, {@link #RotationCoords RotationCoords}, {@link
+ * #TransformCenterSource TransformCenterSource}, {@link #TransformCenter
+ * TransformCenter}, and <code>AccelerationTime</code>.
+ * </li>
+ * <li>
+ * <code>RotateCW</code> - Rotates the view platform clockwise about a Y axis;
+ * the virtual world appears to rotate counter-clockwise. This action is not
+ * assigned by default.
+ * </li>
+ * <li>
+ * <code>ScaleUp</code> - Scales the view platform larger so that the virtual
+ * world appears to grow smaller. This action is not assigned by default.
+ * Related properties are {@link #ScaleSpeed ScaleSpeed},
+ * <code>TransformCenterSource</code>, <code>TransformCenter</code>, and
+ * <code>AccelerationTime</code>.
+ * </li>
+ * <li>
+ * <code>ScaleDown</code> - Scales the view platform smaller so that the
+ * virtual world appears to grow larger. This action is not assigned by
+ * default.
+ * </li>
+ * </ul>
+ * <p>
+ * {@link #ReadAction2D ReadAction2D} sets the action bound to 2D valuator
+ * reads; that is, non-zero values generated by the device when no buttons
+ * have been pressed. If the value is (0.0, 0.0) or below the threshold value
+ * set by {@link #Threshold2D Threshold2D}, then this behavior does nothing;
+ * otherwise, the following actions can be performed:
+ * <ul>
+ * <li>
+ * <code>Rotation</code> - Rotates the view platform. This is the default 2D
+ * valuator action set by this behavior. Related properties are
+ * <code>RotationSpeed</code>, <code>RotationCoords</code>,
+ * <code>TransformCenterSource</code>, and <code>TransformCenter</code>.
+ * </li>
+ * <li>
+ * <code>Translation</code> - Translates the view platform. The translation
+ * occurs relative to the X and Z basis vectors of either the 6DOF sensor or
+ * the view platform if one is not available. The maximum speed is equal to
+ * the product of the <code>TranslationSpeed</code> and
+ * <code>FastSpeedFactor</code> property values.
+ * </li>
+ * <li>
+ * <code>Scale</code> - Scales the view platform smaller with positive Y
+ * values and larger with negative Y values. The effect is to increase the
+ * apparent size of the virtual world when pushing the valuator forwards and
+ * to decrease it when pushing backwards. Related properties are
+ * <code>ScaleSpeed</code>, <code>TransformCenterSource</code>, and
+ * <code>TransformCenter</code>.
+ * </li>
+ * </ul>
+ * <p>
+ * {@link #ButtonAction2D ButtonAction2D} sets an action for a specific button
+ * on the 2D valuator. The available actions are the same as for
+ * <code>ReadAction2D</code>. No actions are bound by default to the 2D
+ * valuator buttons.
+ * <p>
+ * The view transform may be reset to its home transform by pressing a number
+ * of buttons simultaneously on the 6DOF sensor. The minimum number of
+ * buttons that must be pressed is set by {@link #ResetViewButtonCount6D
+ * ResetViewButtonCount6D}. This value must be greater than one; the default
+ * is three. This action may be disabled by setting the property value to
+ * None. The corresponding property for the 2D valuator is {@link
+ * #ResetViewButtonCount2D ResetViewButtonCount2D}, with a default value of
+ * None. Note, however, that the reset view action will be ineffectual if an
+ * action which always modifies the view transform is bound to reads on the
+ * sensor used to reset the view, since the reset transform will get
+ * overwritten by the read action.
+ * <p>
+ * The special value <code>None</code> can in general be assigned to any
+ * button or read action to prevent any defaults from being bound to it.
+ *
+ * @see ConfiguredUniverse
+ * @see SensorEventAgent
+ * @since Java 3D 1.3
+ */
+public class WandViewBehavior extends ViewPlatformBehavior {
+ /**
+ * Indicates a null configuration choice.
+ */
+ public static final int NONE = 0 ;
+
+ /**
+ * Indicates that a 6DOF sensor button action should be bound
+ * to grabbing the view. The default is button 0.
+ */
+ public static final int GRAB_VIEW = 1 ;
+
+ /**
+ * Indicates that a 6DOF sensor button action should be bound
+ * to translating the view forward. The default is button 1.
+ */
+ public static final int TRANSLATE_FORWARD = 2 ;
+
+ /**
+ * Indicates that a 6DOF sensor button action should be bound
+ * to translating the view backward. The default is button 2.
+ */
+ public static final int TRANSLATE_BACKWARD = 3 ;
+
+ /**
+ * Indicates that a 6DOF sensor button action should be bound
+ * to rotate the view plaform counter-clockwise about a Y axis.
+ */
+ public static final int ROTATE_CCW = 4 ;
+
+ /**
+ * Indicates that a 6DOF sensor button action should be bound
+ * to rotate the view platform clockwise about a Y axis.
+ */
+ public static final int ROTATE_CW = 5 ;
+
+ /**
+ * Indicates that a 6DOF sensor button action should be bound
+ * to scaling the view platform larger.
+ */
+ public static final int SCALE_UP = 6 ;
+
+ /**
+ * Indicates that a 6DOF sensor button action should be bound
+ * to scaling the view platform smaller.
+ */
+ public static final int SCALE_DOWN = 7 ;
+
+ /**
+ * Indicates that a 2D sensor button or read action should be bound
+ * to translation.
+ */
+ public static final int TRANSLATION = 8 ;
+
+ /**
+ * Indicates that a 2D sensor button or read action should be bound
+ * to scaling.
+ */
+ public static final int SCALE = 9 ;
+
+ /**
+ * Indicates that a 2D sensor button or read action should be bound
+ * to rotation. The default is to bind rotation to the 2D sensor reads.
+ */
+ public static final int ROTATION = 10 ;
+
+ /**
+ * Indicates that translation, rotation, or scaling speeds are
+ * per frame.
+ */
+ public static final int PER_FRAME = 11 ;
+
+ /**
+ * Use to indicate that translation, rotation, or scaling speeds are per
+ * second. This is the default.
+ */
+ public static final int PER_SECOND = 12 ;
+
+ /**
+ * Indicates that translation speed is in virtual world units.
+ */
+ public static final int VIRTUAL_UNITS = 13 ;
+
+ /**
+ * Indicates that translation speed is in physical world units
+ * (meters per second or per frame). This is the default.
+ */
+ public static final int PHYSICAL_METERS = 14 ;
+
+ /**
+ * Indicates that rotation speed should be in radians.
+ */
+ public static final int RADIANS = 15 ;
+
+ /**
+ * Indicates that rotation speed should be in degrees. This is the
+ * default.
+ */
+ public static final int DEGREES = 16 ;
+
+ /**
+ * Indicates that rotation should occur in view platform
+ * coordinates.
+ */
+ public static final int VIEW_PLATFORM = 17 ;
+
+ /**
+ * Indicates that rotation should occur in head coordinates.
+ */
+ public static final int HEAD = 18 ;
+
+ /**
+ * Indicates that rotation should occur in sensor coordinates.
+ * This is the default.
+ */
+ public static final int SENSOR = 19 ;
+
+ /**
+ * Indicates that rotation or scale should be about a fixed point
+ * in virtual world coordinates.
+ */
+ public static final int VWORLD_FIXED = 20 ;
+
+ /**
+ * Indicates that rotation or scale should be about a 6DOF sensor
+ * hotspot. This is the default.
+ */
+ public static final int HOTSPOT = 21 ;
+
+ /**
+ * Indicates that the 6DOF sensor read action should be bound to
+ * displaying the sensor's echo in the virtual world. This is the
+ * default.
+ */
+ public static final int ECHO = 22 ;
+
+ /**
+ * Indicates that the echo type is a gnomon displaying the
+ * directions of the sensor's local coordinate system axes at the location
+ * of the sensor's hotspot.
+ */
+ public static final int GNOMON = 23 ;
+
+ /**
+ * Indicates that the echo type is a beam extending from the
+ * origin of the sensor's local coordinate system to its hotspot.
+ */
+ public static final int BEAM = 24 ;
+
+ /**
+ * Indicates that a button listener or read listener has not been
+ * set for a particular target. This allows this behavior to use that
+ * target for a default listener.
+ */
+ private static final int UNSET = -1 ;
+
+ private View view = null ;
+ private SensorEventAgent eventAgent = null ;
+ private String sensor6DName = null ;
+ private String sensor2DName = null ;
+ private Shape3D echoGeometry = null ;
+ private BranchGroup echoBranchGroup = null ;
+ private TransformGroup echoTransformGroup = null ;
+ private SensorReadListener echoReadListener6D = null ;
+ private boolean echoBranchGroupAttached = false ;
+ private WakeupCondition wakeupConditions = new WakeupOnElapsedFrames(0) ;
+ private boolean configured = false ;
+
+ // The rest of these private fields are all configurable through
+ // ConfiguredUniverse.
+ private Sensor sensor6D = null ;
+ private Sensor sensor2D = null ;
+ private int x2D = 3 ;
+ private int y2D = 7 ;
+ private double threshold2D = 0.0 ;
+
+ private int readAction6D = UNSET ;
+ private int readAction2D = UNSET ;
+
+ private ArrayList buttonActions6D = new ArrayList() ;
+ private ArrayList buttonActions2D = new ArrayList() ;
+
+ private double translationSpeed = 0.1 ;
+ private int translationUnits = PHYSICAL_METERS ;
+ private int translationTimeBase = PER_SECOND ;
+ private double accelerationTime = 1.0 ;
+ private double constantSpeedTime = 8.0 ;
+ private double fastSpeedFactor = 10.0 ;
+
+ private double rotationSpeed = 180.0 ;
+ private int rotationUnits = DEGREES ;
+ private int rotationTimeBase = PER_SECOND ;
+ private int rotationCoords = SENSOR ;
+
+ private double scaleSpeed = 2.0 ;
+ private int scaleTimeBase = PER_SECOND ;
+
+ private int transformCenterSource = HOTSPOT ;
+ private Point3d transformCenter = new Point3d(0.0, 0.0, 0.0) ;
+
+ private int resetViewButtonCount6D = 3 ;
+ private int resetViewButtonCount2D = NONE ;
+
+ private int echoType = GNOMON ;
+ private double echoSize = 0.01 ;
+ private Color3f echoColor = null ;
+ private float echoTransparency = 0.0f ;
+ private Transform3D nominalSensorRotation = null ;
+
+ /**
+ * Parameterless constructor for this behavior. This is called when this
+ * behavior is instantiated from a configuration file.
+ * <p>
+ * <b>Syntax:</b><br>(NewViewPlatformBehavior <i>&lt;name&gt;</i>
+ * com.sun.j3d.utils.behaviors.vp.WandViewBehavior)
+ */
+ public WandViewBehavior() {
+ // Create an event agent.
+ eventAgent = new SensorEventAgent(this) ;
+
+ // Set a default SchedulingBounds.
+ setSchedulingBounds(new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
+ Double.POSITIVE_INFINITY)) ;
+ }
+
+ /**
+ * Creates a new instance with the specified sensors and echo parameters.
+ * At least one sensor must be non-<code>null</code>.
+ * <p>
+ * This constructor should only be used if either
+ * <code>SimpleUniverse</code> or <code>ConfiguredUniverse</code> is used
+ * to set up the view side of the scene graph, or if it is otherwise to be
+ * attached to a <code>ViewingPlatform</code>. If this behavior is not
+ * instantiated from a configuration file then it must then be explicitly
+ * attached to a <code>ViewingPlatform</code> instance with the
+ * <code>ViewingPlatform.setViewPlatformBehavior</code> method.
+ *
+ * @param sensor6D a six degree of freedom sensor which generates reads
+ * relative to the tracker base in physical units; may be
+ * <code>null</code>
+ * @param sensor2D 2D valuator which generates X and Y reads ranging from
+ * [-1.0 .. +1.0]; may be <code>null</code>
+ * @param echoType either <code>GNOMON</code>, <code>BEAM</code>, or
+ * <code>NONE</code> for the 6DOF sensor echo
+ * @param echoSize the width of the 6DOF sensor echo in physical meters;
+ * ignored if echoType is <code>NONE</code>
+ */
+ public WandViewBehavior(Sensor sensor6D, Sensor sensor2D,
+ int echoType, double echoSize) {
+ this() ;
+ this.sensor6D = sensor6D ;
+ this.sensor2D = sensor2D ;
+ this.echoType = echoType ;
+ this.echoSize = echoSize ;
+ }
+
+ /**
+ * Creates a new instance with the specified sensors and a 6DOF sensor
+ * echo parented by the specified <code>TransformGroup</code>. At least
+ * one sensor must be non-<code>null</code>.
+ * <p>
+ * This constructor should only be used if either
+ * <code>SimpleUniverse</code> or <code>ConfiguredUniverse</code> is used
+ * to set up the view side of the scene graph, or if it is otherwise to be
+ * attached to a <code>ViewingPlatform</code>. If this behavior is not
+ * instantiated from a configuration file then it must then be explicitly
+ * attached to a <code>ViewingPlatform</code> instance with the
+ * <code>ViewingPlatform.setViewPlatformBehavior</code> method.
+ * <p>
+ * If the echo <code>TransformGroup</code> is non-<code>null</code>, it
+ * will be added to a new <code>BranchGroup</code> and attached to the
+ * <code>ViewingPlatform</code>, where its transform will be updated in
+ * response to the sensor reads. Capabilities to allow writing its
+ * transform and to read, write, and extend its children will be set. The
+ * echo geometry is assumed to incorporate the position and orientation of
+ * the 6DOF sensor hotspot.
+ *
+ * @param sensor6D a six degree of freedom sensor which generates reads
+ * relative to the tracker base in physical units; may be
+ * <code>null</code>
+ * @param sensor2D 2D valuator which generates X and Y reads ranging from
+ * [-1.0 .. +1.0]; may be <code>null</code>
+ * @param echo a <code>TransformGroup</code> containing the visible echo
+ * which will track the 6DOF sensor's position and orientation, or
+ * <code>null</code> for no echo
+ */
+ public WandViewBehavior(Sensor sensor6D, Sensor sensor2D,
+ TransformGroup echo) {
+ this() ;
+ this.sensor6D = sensor6D ;
+ this.sensor2D = sensor2D ;
+ if (echo != null) {
+ echo.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE) ;
+ echo.setCapability(Group.ALLOW_CHILDREN_READ) ;
+ echo.setCapability(Group.ALLOW_CHILDREN_WRITE) ;
+ echo.setCapability(Group.ALLOW_CHILDREN_EXTEND) ;
+ }
+ this.echoTransformGroup = echo ;
+ }
+
+ /**
+ * Creates a new instance with the specified sensors and a 6DOF sensor
+ * echo parented by the specified <code>TransformGroup</code>. At least
+ * one sensor must be non-<code>null</code>.
+ * <p>
+ * This constructor should only be used if <code>SimpleUniverse</code> or
+ * <code>ConfiguredUniverse</code> is <i>not</i> used to set up the view
+ * side of the scene graph. The application must set up the view side
+ * itself and supply references to the <code>View</code> and the
+ * <code>TransformGroup</code> containing the view platform transform.
+ * <code>ViewingPlatform.setViewPlatformBehavior</code> must <i>not</i>
+ * be called, and this behavior must be explicitly added to the virtual
+ * universe by the application.
+ * <p>
+ * If the echo <code>TransformGroup</code> is non-<code>null</code>, it
+ * will only be used to update its associated transform with the position
+ * and orientation of a 6DOF sensor (if supplied). The application is
+ * responsible for adding the echo to the virtual universe. The echo
+ * geometry is assumed to incorporate the position and orientation of the
+ * 6DOF sensor hotspot.
+ *
+ * @param sensor6D a six degree of freedom sensor which generates reads
+ * relative to the tracker base in physical units; may be
+ * <code>null</code>
+ * @param sensor2D 2D valuator which generates X and Y reads ranging from
+ * [-1.0 .. +1.0]; may be <code>null</code>
+ * @param view a reference to the <code>View</code> attached to the
+ * <code>ViewPlatform</code> to be manipulated by this behavior
+ * @param viewTransform a <code>TransformGroup</code> containing the view
+ * platform transform; appropriate capabilities to update the transform
+ * must be set
+ * @param homeTransform a <code>Transform3D</code> containing the
+ * view transform to be used when the view is reset; may be
+ * <code>null</code> for identity
+ * @param echo a <code>TransformGroup</code> containing the visible echo
+ * which will track the 6DOF sensor's position and orientation, or
+ * <code>null</code> for no echo; appropriate capabilities to update the
+ * transform must be set
+ */
+ public WandViewBehavior(Sensor sensor6D, Sensor sensor2D,
+ View view, TransformGroup viewTransform,
+ Transform3D homeTransform, TransformGroup echo) {
+ this() ;
+ this.sensor6D = sensor6D ;
+ this.sensor2D = sensor2D ;
+ this.view = view ;
+ this.targetTG = viewTransform ;
+ this.echoTransformGroup = echo ;
+
+ if (homeTransform == null)
+ setHomeTransform(new Transform3D()) ;
+ else
+ setHomeTransform(homeTransform) ;
+ }
+
+ /**
+ * Initializes and configures this behavior.
+ * NOTE: Applications should <i>not</i> call this method. It is called by
+ * the Java 3D behavior scheduler.
+ */
+ public void initialize() {
+ // Don't configure the sensors and echo after the first time.
+ if (!configured) {
+ configureSensorActions() ;
+
+ // Configure an echo only if a ViewingPlatform is in use.
+ if (vp != null) {
+ if (echoTransformGroup == null &&
+ sensor6D != null && readAction6D == ECHO) {
+ configureEcho() ;
+ }
+ if (echoTransformGroup != null) {
+ echoBranchGroup = new BranchGroup() ;
+ echoBranchGroup.setCapability
+ (BranchGroup.ALLOW_DETACH) ;
+ echoBranchGroup.setCapability
+ (BranchGroup.ALLOW_CHILDREN_READ) ;
+ echoBranchGroup.setCapability
+ (BranchGroup.ALLOW_CHILDREN_WRITE) ;
+
+ echoBranchGroup.addChild(echoTransformGroup) ;
+ echoBranchGroup.compile() ;
+ }
+ attachEcho() ;
+ }
+ configured = true ;
+ }
+ wakeupOn(wakeupConditions) ;
+ }
+
+ /**
+ * Processes a stimulus meant for this behavior.
+ * NOTE: Applications should <i>not</i> call this method. It is called by
+ * the Java 3D behavior scheduler.
+ */
+ public void processStimulus(Enumeration criteria) {
+ // Invoke the sensor event dispatcher.
+ eventAgent.dispatchEvents() ;
+
+ // Wake up on the next frame.
+ wakeupOn(wakeupConditions) ;
+ }
+
+ /**
+ * Enables or disables this behavior. The default state is enabled.
+ * @param enable true or false to enable or disable this behavior
+ */
+ public void setEnable(boolean enable) {
+ if (enable == getEnable()) {
+ return ;
+ }
+ else if (enable) {
+ attachEcho() ;
+ }
+ else {
+ detachEcho() ;
+ }
+ super.setEnable(enable) ;
+ }
+
+ /**
+ * Sets the <code>ViewingPlatform</code> for this behavior. If a subclass
+ * overrides this method, it must call
+ * <code>super.setViewingPlatform(vp)</code>. NOTE: Applications should
+ * <i>not</i> call this method. It is called by the
+ * <code>ViewingPlatform</code>.
+ */
+ public void setViewingPlatform(ViewingPlatform vp) {
+ super.setViewingPlatform(vp) ;
+ if (vp == null) {
+ detachEcho() ;
+ return ;
+ }
+
+ Viewer[] viewers = vp.getViewers() ;
+ if (viewers != null) {
+ // Get the View from the first Viewer attached to the
+ // ViewingPlatform. Multiple Viewers are not supported.
+ if (viewers.length != 0 && viewers[0] != null)
+ view = viewers[0].getView() ;
+
+ if (viewers.length > 1)
+ throw new RuntimeException("multiple Viewers not supported") ;
+ }
+ if (view == null) {
+ // Fallback to the first View attached to a live ViewPlatform.
+ view = getView() ;
+ }
+ if (view == null) {
+ // This behavior requires a view. Bail.
+ throw new RuntimeException("a view is not available") ;
+ }
+
+ // Get the top-most TransformGroup in the ViewingPlatform.
+ // ViewPlatformBehavior retrieves the bottom-most which won't work
+ // if there are multiple TransformGroups.
+ targetTG = vp.getMultiTransformGroup().getTransformGroup(0) ;
+
+ // Should be an API for checking if homeTransform is null.
+ if (homeTransform == null)
+ setHomeTransform(new Transform3D()) ;
+
+ attachEcho() ;
+ }
+
+ /**
+ * Attaches the echo BranchGroup to the ViewingPlatform if appropriate.
+ */
+ private void attachEcho() {
+ if (vp != null &&
+ echoBranchGroup != null && !echoBranchGroupAttached) {
+ vp.addChild(echoBranchGroup) ;
+ echoBranchGroupAttached = true ;
+ }
+ }
+
+ /**
+ * Detaches the echo BranchGroup from the ViewingPlatform if appropriate.
+ */
+ private void detachEcho() {
+ if (echoBranchGroup != null && echoBranchGroupAttached) {
+ echoBranchGroup.detach() ;
+ echoBranchGroupAttached = false ;
+ }
+ }
+
+ /**
+ * Creates the sensor listeners for a 6DOF sensor and/or a 2D valuator
+ * sensor using the predefined button and read listeners and the
+ * configured action bindings.
+ * <p>
+ * This is invoked the first time <code>initialize</code> is called. This
+ * method can be overridden by subclasses to modify the configured
+ * bindings or introduce other configuration parameters.
+ */
+ protected void configureSensorActions() {
+ SensorButtonListener[] sbls ;
+ int buttonCount, buttonActionCount ;
+
+ SimpleUniverse universe = null ;
+ if (vp != null) universe = vp.getUniverse() ;
+ if (universe != null && universe instanceof ConfiguredUniverse) {
+ // Check if sensors were instantiated from a config file.
+ Map sensorMap = ((ConfiguredUniverse)universe).getNamedSensors() ;
+
+ if (sensor2D == null && sensor2DName != null) {
+ sensor2D = (Sensor)sensorMap.get(sensor2DName) ;
+ if (sensor2D == null)
+ throw new IllegalArgumentException
+ ("\nsensor " + sensor2DName + " not found") ;
+ }
+
+ if (sensor6D == null && sensor6DName != null) {
+ sensor6D = (Sensor)sensorMap.get(sensor6DName) ;
+ if (sensor6D == null)
+ throw new IllegalArgumentException
+ ("\nsensor " + sensor6DName + " not found") ;
+ }
+ }
+
+ if (sensor6D != null) {
+ // Assign default read action.
+ if (readAction6D == UNSET)
+ readAction6D = ECHO ;
+
+ // Register the read listener.
+ if (readAction6D == ECHO) {
+ echoReadListener6D = new EchoReadListener6D() ;
+ eventAgent.addSensorReadListener
+ (sensor6D, echoReadListener6D) ;
+ }
+
+ // Check for button range.
+ buttonCount = sensor6D.getSensorButtonCount() ;
+ buttonActionCount = buttonActions6D.size() ;
+ if (buttonActionCount > buttonCount)
+ throw new IllegalArgumentException
+ ("\nbutton index " + (buttonActionCount-1) +
+ " >= number of buttons (" + buttonCount +")") ;
+
+ // Assign default button actions.
+ if (buttonCount > 2 &&
+ (buttonActionCount < 3 || buttonActions6D.get(2) == null))
+ setButtonAction6D(2, TRANSLATE_BACKWARD) ;
+ if (buttonCount > 1 &&
+ (buttonActionCount < 2 || buttonActions6D.get(1) == null))
+ setButtonAction6D(1, TRANSLATE_FORWARD) ;
+ if (buttonCount > 0 &&
+ (buttonActionCount < 1 || buttonActions6D.get(0) == null))
+ setButtonAction6D(0, GRAB_VIEW) ;
+
+ buttonActionCount = buttonActions6D.size() ;
+ if (buttonActionCount > 0) {
+ // Set up the button listener array.
+ sbls = new SensorButtonListener[buttonCount] ;
+ for (int i = 0 ; i < buttonActionCount ; i++) {
+ Integer button = (Integer)buttonActions6D.get(i) ;
+ if (button != null) {
+ int action = button.intValue() ;
+ if (action == NONE)
+ sbls[i] = null ;
+ else if (action == GRAB_VIEW)
+ sbls[i] = new GrabViewListener6D() ;
+ else if (action == TRANSLATE_FORWARD)
+ sbls[i] = new TranslationListener6D(false) ;
+ else if (action == TRANSLATE_BACKWARD)
+ sbls[i] = new TranslationListener6D(true) ;
+ else if (action == ROTATE_CCW)
+ sbls[i] = new RotationListener6D(false) ;
+ else if (action == ROTATE_CW)
+ sbls[i] = new RotationListener6D(true) ;
+ else if (action == SCALE_UP)
+ sbls[i] = new ScaleListener6D(false) ;
+ else if (action == SCALE_DOWN)
+ sbls[i] = new ScaleListener6D(true) ;
+ }
+ }
+ // Register the button listeners.
+ eventAgent.addSensorButtonListeners(sensor6D, sbls) ;
+ }
+
+ // Check for reset view action.
+ if (resetViewButtonCount6D != NONE) {
+ SensorInputAdaptor r =
+ new ResetViewListener(sensor6D, resetViewButtonCount6D) ;
+ eventAgent.addSensorButtonListener(sensor6D, r) ;
+ eventAgent.addSensorReadListener(sensor6D, r) ;
+ }
+ }
+
+ if (sensor2D != null) {
+ // Assign default read action
+ if (readAction2D == UNSET)
+ readAction2D = ROTATION ;
+
+ // Register the read listener.
+ if (readAction2D == ROTATION) {
+ SensorReadListener r =
+ new RotationListener2D(sensor2D, sensor6D) ;
+ eventAgent.addSensorReadListener(sensor2D, r) ;
+ }
+ else if (readAction2D == TRANSLATION) {
+ SensorReadListener r =
+ new TranslationListener2D(sensor2D, sensor6D) ;
+ eventAgent.addSensorReadListener(sensor2D, r) ;
+ }
+ else if (readAction2D == SCALE) {
+ SensorReadListener r =
+ new ScaleListener2D(sensor2D, sensor6D) ;
+ eventAgent.addSensorReadListener(sensor2D, r) ;
+ }
+
+ // Check for button range.
+ buttonCount = sensor2D.getSensorButtonCount() ;
+ buttonActionCount = buttonActions2D.size() ;
+ if (buttonActionCount > buttonCount)
+ throw new IllegalArgumentException
+ ("\nbutton index " + (buttonActionCount-1) +
+ " >= number of buttons (" + buttonCount +")") ;
+
+ // No default button actions are defined for the 2D sensor.
+ if (buttonActionCount > 0) {
+ // Set up the button listener array.
+ sbls = new SensorButtonListener[buttonCount] ;
+ for (int i = 0 ; i < buttonActionCount ; i++) {
+ Integer button = (Integer)buttonActions2D.get(i) ;
+ if (button != null) {
+ int action = button.intValue() ;
+ if (action == NONE)
+ sbls[i] = null ;
+ else if (action == ROTATION)
+ sbls[i] = new RotationListener2D
+ (sensor2D, sensor6D) ;
+ else if (action == TRANSLATION)
+ sbls[i] = new TranslationListener2D
+ (sensor2D, sensor6D) ;
+ else if (action == SCALE)
+ sbls[i] = new ScaleListener2D
+ (sensor2D, sensor6D) ;
+ }
+ }
+ // Register the button listeners.
+ eventAgent.addSensorButtonListeners(sensor2D, sbls) ;
+ }
+
+ // Check for reset view action.
+ if (resetViewButtonCount2D != NONE) {
+ SensorInputAdaptor r =
+ new ResetViewListener(sensor2D, resetViewButtonCount2D) ;
+ eventAgent.addSensorButtonListener(sensor2D, r) ;
+ eventAgent.addSensorReadListener(sensor2D, r) ;
+ }
+ }
+ }
+
+ /**
+ * Creates a 6DOF sensor echo according to configuration parameters. This
+ * is done only if a 6DOF sensor has been specified, the 6DOF sensor read
+ * action has been set to echo the sensor position, the echo transform
+ * group has not already been set, and a ViewingPlatform is in use. This
+ * is invoked the first time <code>initialize</code> is called to set this
+ * behavior live, but before the echo transform group is added to a
+ * <code>BranchGroup</code> and made live. This method can be overridden
+ * to support other echo geometry.
+ */
+ protected void configureEcho() {
+ Point3d hotspot = new Point3d() ;
+ sensor6D.getHotspot(hotspot) ;
+
+ if (echoType == GNOMON) {
+ Transform3D gnomonTransform = new Transform3D() ;
+ if (nominalSensorRotation != null) {
+ gnomonTransform.set(nominalSensorRotation) ;
+ gnomonTransform.invert() ;
+ }
+ gnomonTransform.setTranslation(new Vector3d(hotspot)) ;
+ echoGeometry = new SensorGnomonEcho
+ (gnomonTransform, 0.1 * echoSize, 0.5 * echoSize, true) ;
+ }
+ else if (echoType == BEAM) {
+ echoGeometry = new SensorBeamEcho(hotspot, echoSize, true) ;
+ }
+
+ if (echoGeometry != null) {
+ Appearance a = echoGeometry.getAppearance() ;
+ if (echoColor != null) {
+ Material m = a.getMaterial() ;
+ m.setDiffuseColor(echoColor) ;
+ }
+ if (echoTransparency != 0.0f) {
+ TransparencyAttributes ta = a.getTransparencyAttributes() ;
+ ta.setTransparencyMode(TransparencyAttributes.BLENDED) ;
+ ta.setTransparency(echoTransparency) ;
+ // Use order independent additive blend for gnomon.
+ if (echoGeometry instanceof SensorGnomonEcho)
+ ta.setDstBlendFunction(TransparencyAttributes.BLEND_ONE) ;
+ }
+ echoTransformGroup = new TransformGroup() ;
+ echoTransformGroup.setCapability
+ (TransformGroup.ALLOW_TRANSFORM_WRITE) ;
+ echoTransformGroup.setCapability(Group.ALLOW_CHILDREN_READ) ;
+ echoTransformGroup.setCapability(Group.ALLOW_CHILDREN_WRITE) ;
+ echoTransformGroup.setCapability(Group.ALLOW_CHILDREN_EXTEND) ;
+ echoTransformGroup.addChild(echoGeometry) ;
+ }
+ }
+
+ /**
+ * A base class for implementing some of this behavior's listeners.
+ */
+ public class ListenerBase extends SensorInputAdaptor {
+ /**
+ * The initial transform from view platform coordinates to virtual
+ * world coordinates, set by <code>initAction</code>.
+ */
+ protected Transform3D viewPlatformToVworld = new Transform3D() ;
+
+ /**
+ * The initial transform from tracker base coordinates to virtual
+ * world coordinates, set by <code>initAction</code>.
+ */
+ protected Transform3D trackerToVworld = new Transform3D() ;
+
+ /**
+ * The initial transform from sensor coordinates to virtual
+ * world coordinates, set by <code>initAction</code>.
+ */
+ protected Transform3D sensorToVworld = new Transform3D() ;
+
+ /**
+ * The initial transform from sensor coordinates to tracker base
+ * coordinates, set by <code>initAction</code>.
+ */
+ protected Transform3D sensorToTracker = new Transform3D() ;
+
+ // Private fields.
+ private Transform3D trackerToSensor = new Transform3D() ;
+ private boolean active = false ;
+
+ // Misc. temporary objects.
+ private double[] s3Tmp = new double[3] ;
+ private double[] m16Tmp = new double[16] ;
+ private Vector3d v3dTmp = new Vector3d() ;
+ private Transform3D t3dTmp = new Transform3D() ;
+
+ /**
+ * Initializes the listener action. Subclasses must call this before
+ * starting the action, either from <code>pressed</code> or when a 2D
+ * valuator exits the deadzone threshold.
+ *
+ * @param s reference to a 6DOF sensor if used by the listener; may
+ * be <code>null</code>
+ */
+ protected void initAction(Sensor s) {
+ targetTG.getTransform(viewPlatformToVworld) ;
+ active = true ;
+ if (s == null) return ;
+
+ // Kludge to get the static trackerToVworld for this
+ // frame. This is computed from the two separate sensor reads
+ // below, which are close enough to identical to work. The
+ // Java 3D View class needs a getTrackerBaseToVworld() method
+ // (see Java 3D RFE 4676808).
+ s.getRead(sensorToTracker) ;
+ view.getSensorToVworld(s, sensorToVworld) ;
+
+ trackerToSensor.invert(sensorToTracker) ;
+ trackerToVworld.mul(sensorToVworld, trackerToSensor) ;
+ }
+
+ /**
+ * Ends the action. Subclasses must be call this from
+ * <code>released</code> or when a 2D valuator enters the deadzone
+ * threshold.
+ *
+ * @param s reference to a 6DOF sensor if used by the listener; may
+ * be <code>null</code>
+ */
+ protected void endAction(Sensor s) {
+ active = false ;
+ }
+
+ /**
+ * Returns true if the listener is currently active; that is, if
+ * <code>initAction</code> has been called but not yet
+ * <code>endAction</code>.
+ *
+ * @return true if the listener is active, false otherwise
+ */
+ protected boolean isActive() {
+ return active ;
+ }
+
+ public void pressed(SensorEvent e) {
+ initAction(e.getSensor()) ;
+ }
+
+ public void released(SensorEvent e) {
+ endAction(e.getSensor()) ;
+ }
+
+ /**
+ * Gets the physical to virtual scale.
+ */
+ protected double getPhysicalToVirtualScale() {
+ view.getCanvas3D(0).getImagePlateToVworld(t3dTmp) ;
+ t3dTmp.get(m16Tmp) ;
+ return Math.sqrt(m16Tmp[0]*m16Tmp[0] +
+ m16Tmp[1]*m16Tmp[1] +
+ m16Tmp[2]*m16Tmp[2]) ;
+ }
+
+ /**
+ * Gets the scale from physical units to view platform units.
+ */
+ protected double getPhysicalToViewPlatformScale() {
+ double vpToVirtualScale ;
+
+ targetTG.getTransform(t3dTmp) ;
+ t3dTmp.get(m16Tmp) ;
+ vpToVirtualScale = Math.sqrt(m16Tmp[0]*m16Tmp[0] +
+ m16Tmp[1]*m16Tmp[1] +
+ m16Tmp[2]*m16Tmp[2]) ;
+
+ return getPhysicalToVirtualScale() / vpToVirtualScale ;
+ }
+
+ /**
+ * Translates a coordinate system.
+ *
+ * @param transform the coordinate system to be translated
+ * @param translation the vector by which to translate
+ */
+ protected void translateTransform(Transform3D transform,
+ Vector3d translation) {
+ transform.get(v3dTmp) ;
+ v3dTmp.add(translation) ;
+ transform.setTranslation(v3dTmp) ;
+ }
+
+ /**
+ * Transforms the target coordinate system about a center point.
+ * This can be used for rotation and scaling.
+ *
+ * @param target the coordinate system to transform
+ * @param center the center point about which to transform
+ * @param transform the transform to apply
+ */
+ protected void transformAboutCenter
+ (Transform3D target, Point3d center, Transform3D transform) {
+
+ // Translate to the center.
+ target.get(v3dTmp) ;
+ v3dTmp.sub(center) ;
+ target.setTranslation(v3dTmp) ;
+
+ // Apply the transform.
+ target.mul(transform, target) ;
+
+ // Translate back.
+ target.get(v3dTmp) ;
+ v3dTmp.add(center) ;
+ target.setTranslation(v3dTmp) ;
+ }
+
+ /**
+ * Equalizes the scale factors in the view tranform, which must be
+ * congruent. If successful, the <code>ViewingPlatform
+ * TransformGroup</code> is updated; otherwise, its transform is reset
+ * to the home transform. This should be called if multiple
+ * incremental scale factors are applied to the view transform.
+ *
+ * @param viewPlatformToVworld the view transform
+ */
+ protected void conditionViewScale(Transform3D viewPlatformToVworld) {
+ viewPlatformToVworld.normalize() ;
+ viewPlatformToVworld.get(m16Tmp) ;
+
+ s3Tmp[0] = m16Tmp[0]*m16Tmp[0] +
+ m16Tmp[4]*m16Tmp[4] + m16Tmp[8]*m16Tmp[8] ;
+ s3Tmp[1] = m16Tmp[1]*m16Tmp[1] +
+ m16Tmp[5]*m16Tmp[5] + m16Tmp[9]*m16Tmp[9] ;
+ s3Tmp[2] = m16Tmp[2]*m16Tmp[2] +
+ m16Tmp[6]*m16Tmp[6] + m16Tmp[10]*m16Tmp[10] ;
+
+ if (s3Tmp[0] == s3Tmp[1] && s3Tmp[0] == s3Tmp[2])
+ return ;
+
+ s3Tmp[0] = Math.sqrt(s3Tmp[0]) ;
+ s3Tmp[1] = Math.sqrt(s3Tmp[1]) ;
+ s3Tmp[2] = Math.sqrt(s3Tmp[2]) ;
+
+ int closestToOne = 0 ;
+ if (Math.abs(s3Tmp[1] - 1.0) < Math.abs(s3Tmp[0] - 1.0))
+ closestToOne = 1 ;
+ if (Math.abs(s3Tmp[2] - 1.0) < Math.abs(s3Tmp[closestToOne] - 1.0))
+ closestToOne = 2 ;
+
+ double scale ;
+ for (int i = 0 ; i < 3 ; i++) {
+ if (i == closestToOne) continue ;
+ scale = s3Tmp[closestToOne] / s3Tmp[i] ;
+ m16Tmp[i+0] *= scale ;
+ m16Tmp[i+4] *= scale ;
+ m16Tmp[i+8] *= scale ;
+ }
+
+ // Set the view transform and bail out if unsuccessful.
+ viewPlatformToVworld.set(m16Tmp) ;
+ if ((viewPlatformToVworld.getType() & Transform3D.CONGRUENT) == 0)
+ goHome() ;
+ else
+ targetTG.setTransform(viewPlatformToVworld) ;
+ }
+ }
+
+ /**
+ * Implements a 6DOF sensor button listener to directly manipulate the
+ * view platform transform. The view platform moves in inverse response
+ * to the sensor's position and orientation to give the effect of
+ * attaching the virtual world to the sensor's echo.
+ * @see #setButtonAction6D
+ */
+ public class GrabViewListener6D extends ListenerBase {
+ private Transform3D t3d = new Transform3D() ;
+ private Transform3D initialVworldToSensor = new Transform3D() ;
+
+ public void pressed(SensorEvent e) {
+ initAction(e.getSensor()) ;
+
+ // Save the inverse of the initial sensorToVworld.
+ initialVworldToSensor.invert(sensorToVworld) ;
+ }
+
+ public void dragged(SensorEvent e) {
+ // Get sensor read relative to the static view at the time of the
+ // button-down.
+ Sensor s = e.getSensor() ;
+ s.getRead(sensorToTracker) ;
+ sensorToVworld.mul(trackerToVworld, sensorToTracker) ;
+
+ // Solve for T, where T x initialSensorToVworld = sensorToVworld
+ t3d.mul(sensorToVworld, initialVworldToSensor) ;
+
+ // Move T to the view side by inverting it, and then applying it
+ // to the static view transform.
+ t3d.invert() ;
+ t3d.mul(viewPlatformToVworld) ;
+ targetTG.setTransform(t3d) ;
+ }
+ }
+
+ /**
+ * Implements a 6DOF sensor button listener that translates the view
+ * platform along the direction the sensor is pointing.
+ * @see #setButtonAction6D
+ * @see #setTranslationSpeed
+ * @see #setAccelerationTime
+ * @see #setConstantSpeedTime
+ * @see #setFastSpeedFactor
+ */
+ public class TranslationListener6D extends ListenerBase {
+ private long buttonDownTime ;
+ private double speedScaled ;
+ private double interval0 ;
+ private double interval1 ;
+ private double interval2 ;
+ private Vector3d v3d = new Vector3d() ;
+
+ /**
+ * Construct a new translation button listener for a 6DOF sensor.
+ *
+ * @param reverse if true, translate the view platform backwards;
+ * otherwise, translate the view platform forwards
+ */
+ public TranslationListener6D(boolean reverse) {
+ // Compute translation speed intervals.
+ interval0 = accelerationTime ;
+ interval1 = interval0 + constantSpeedTime ;
+ interval2 = interval1 + accelerationTime ;
+
+ // Apply virtual to physical scale if needed.
+ if (translationUnits == VIRTUAL_UNITS)
+ speedScaled = translationSpeed / getPhysicalToVirtualScale() ;
+ else
+ speedScaled = translationSpeed ;
+
+ if (reverse) {
+ speedScaled = -speedScaled ;
+ }
+ }
+
+ public void pressed(SensorEvent e) {
+ initAction(e.getSensor()) ;
+ buttonDownTime = e.getTime() ;
+ }
+
+ public void dragged(SensorEvent e) {
+ long time = e.getTime() ;
+ long lastTime = e.getLastTime() ;
+ double currSpeed, transTime ;
+ double frameTime = 1.0 ;
+ if (translationTimeBase == PER_SECOND)
+ frameTime = (time - lastTime) / 1e9 ;
+
+ // Compute speed based on acceleration intervals.
+ transTime = (time - buttonDownTime) / 1e9 ;
+ if (transTime <= interval0) {
+ currSpeed = (transTime / accelerationTime) * speedScaled ;
+ }
+ else if (transTime > interval1 && transTime < interval2) {
+ currSpeed = ((((transTime-interval1) / accelerationTime) *
+ (fastSpeedFactor-1.0)) + 1.0) * speedScaled ;
+ }
+ else if (transTime >= interval2) {
+ currSpeed = fastSpeedFactor * speedScaled ;
+ }
+ else {
+ currSpeed = speedScaled ;
+ }
+
+ // Transform the translation direction (0, 0, -1).
+ v3d.set(0.0, 0.0, -1.0) ;
+ if (nominalSensorRotation != null)
+ nominalSensorRotation.transform(v3d) ;
+
+ // To avoid echo frame lag, compute sensorToVworld based on
+ // computed trackerToVworld. getSensorToVworld() isn't
+ // current for this frame.
+ Sensor s = e.getSensor() ;
+ s.getRead(sensorToTracker) ;
+ sensorToVworld.mul(trackerToVworld, sensorToTracker) ;
+ sensorToVworld.transform(v3d) ;
+
+ // Translate the view platform.
+ v3d.scale(frameTime * currSpeed) ;
+ translateTransform(viewPlatformToVworld, v3d) ;
+ targetTG.setTransform(viewPlatformToVworld) ;
+
+ // Translate trackerToVworld.
+ translateTransform(trackerToVworld, v3d) ;
+
+ if (readAction6D == ECHO) {
+ // Translate sensor echo to compensate for the new view
+ // platform movement.
+ translateTransform(sensorToVworld, v3d) ;
+ updateEcho(s, sensorToVworld) ;
+ }
+ }
+ }
+
+ /**
+ * Implements a 6DOF sensor button listener that rotates the view platform
+ * about a Y axis. This axis can be relative to the sensor, user head, or
+ * view platform. The rotation center can be the sensor hotspot or a
+ * fixed point in virtual world coordinates.
+ *
+ * @see #setButtonAction6D
+ * @see #setRotationCoords
+ * @see #setTransformCenterSource
+ * @see #setTransformCenter
+ * @see #setRotationSpeed
+ * @see #setAccelerationTime
+ */
+ public class RotationListener6D extends ListenerBase {
+ private boolean reverse ;
+ private long buttonDownTime ;
+ private Vector3d axis = new Vector3d() ;
+ private Point3d center = new Point3d() ;
+ private Transform3D t3d = new Transform3D() ;
+ private AxisAngle4d aa4d = new AxisAngle4d() ;
+ private Transform3D headToVworld = new Transform3D() ;
+ private double speedScaled ;
+
+ protected void initAction(Sensor s) {
+ super.initAction(s) ;
+ if (rotationCoords == HEAD) {
+ view.setUserHeadToVworldEnable(true) ;
+ }
+ }
+
+ protected void endAction(Sensor s) {
+ super.endAction(s) ;
+ viewPlatformToVworld.normalize() ;
+ targetTG.setTransform(viewPlatformToVworld) ;
+ if (rotationCoords == HEAD) {
+ view.setUserHeadToVworldEnable(false) ;
+ }
+ }
+
+ /**
+ * Construct a new rotation button listener for a 6DOF sensor.
+ *
+ * @param reverse if true, rotate clockwise; otherwise, rotate
+ * counter-clockwise
+ */
+ public RotationListener6D(boolean reverse) {
+ this.reverse = reverse ;
+ if (rotationUnits == DEGREES)
+ speedScaled = rotationSpeed * Math.PI / 180.0 ;
+ else
+ speedScaled = rotationSpeed ;
+ }
+
+ public void pressed(SensorEvent e) {
+ initAction(e.getSensor()) ;
+ buttonDownTime = e.getTime() ;
+ }
+
+ public void dragged(SensorEvent e) {
+ long time = e.getTime() ;
+ long lastTime = e.getLastTime() ;
+ double currSpeed, transTime ;
+ double frameTime = 1.0 ;
+ if (rotationTimeBase == PER_SECOND)
+ frameTime = (time - lastTime) / 1e9 ;
+
+ // Compute speed based on acceleration interval.
+ transTime = (time - buttonDownTime) / 1e9 ;
+ if (transTime <= accelerationTime) {
+ currSpeed = (transTime / accelerationTime) * speedScaled ;
+ }
+ else {
+ currSpeed = speedScaled ;
+ }
+
+ // Set the rotation axis.
+ if (reverse)
+ axis.set(0.0, -1.0, 0.0) ;
+ else
+ axis.set(0.0, 1.0, 0.0) ;
+
+ // To avoid echo frame lag, compute sensorToVworld based on
+ // computed trackerToVworld. getSensorToVworld() isn't current
+ // for this frame.
+ Sensor s = e.getSensor() ;
+ s.getRead(sensorToTracker) ;
+ sensorToVworld.mul(trackerToVworld, sensorToTracker) ;
+
+ // Transform rotation axis into target coordinate system.
+ if (rotationCoords == SENSOR) {
+ if (nominalSensorRotation != null)
+ nominalSensorRotation.transform(axis) ;
+
+ sensorToVworld.transform(axis) ;
+ }
+ else if (rotationCoords == HEAD) {
+ view.getUserHeadToVworld(headToVworld) ;
+ headToVworld.transform(axis) ;
+ }
+ else {
+ viewPlatformToVworld.transform(axis) ;
+ }
+
+ // Get the rotation center.
+ if (transformCenterSource == HOTSPOT) {
+ s.getHotspot(center) ;
+ sensorToVworld.transform(center) ;
+ }
+ else {
+ center.set(transformCenter) ;
+ }
+
+ // Construct origin-based rotation about axis.
+ aa4d.set(axis, currSpeed * frameTime) ;
+ t3d.set(aa4d) ;
+
+ // Apply the rotation to the view platform.
+ transformAboutCenter(viewPlatformToVworld, center, t3d) ;
+ targetTG.setTransform(viewPlatformToVworld) ;
+
+ // Apply the rotation to trackerToVworld.
+ transformAboutCenter(trackerToVworld, center, t3d) ;
+
+ if (readAction6D == ECHO) {
+ // Transform sensor echo to compensate for the new view
+ // platform movement.
+ transformAboutCenter(sensorToVworld, center, t3d) ;
+ updateEcho(s, sensorToVworld) ;
+ }
+ }
+ }
+
+ /**
+ * Implements a 6DOF sensor button listener that scales the view platform.
+ * The center of scaling can be the sensor hotspot or a fixed location in
+ * virtual world coordinates.
+ *
+ * @see #setButtonAction6D
+ * @see #setTransformCenterSource
+ * @see #setTransformCenter
+ * @see #setScaleSpeed
+ * @see #setAccelerationTime
+ */
+ public class ScaleListener6D extends ListenerBase {
+ private double direction ;
+ private long buttonDownTime ;
+ private Point3d center = new Point3d() ;
+ private Transform3D t3d = new Transform3D() ;
+
+ protected void endAction(Sensor s) {
+ super.endAction(s) ;
+ conditionViewScale(viewPlatformToVworld) ;
+ }
+
+ /**
+ * Construct a new scale button listener for a 6DOF sensor.
+ *
+ * @param reverse if true, scale the view platform smaller; otherwise,
+ * scale the view platform larger
+ */
+ public ScaleListener6D(boolean reverse) {
+ if (reverse)
+ direction = -1.0 ;
+ else
+ direction = 1.0 ;
+ }
+
+ public void pressed(SensorEvent e) {
+ initAction(e.getSensor()) ;
+ buttonDownTime = e.getTime() ;
+ }
+
+ public void dragged(SensorEvent e) {
+ long time = e.getTime() ;
+ long lastTime = e.getLastTime() ;
+ double scale, exp, transTime ;
+ double frameTime = 1.0 ;
+ if (scaleTimeBase == PER_SECOND)
+ frameTime = (time - lastTime) / 1e9 ;
+
+ // Compute speed based on acceleration interval.
+ transTime = (time - buttonDownTime) / 1e9 ;
+ if (transTime <= accelerationTime) {
+ exp = (transTime / accelerationTime) * frameTime * direction ;
+ }
+ else {
+ exp = frameTime * direction ;
+ }
+ scale = Math.pow(scaleSpeed, exp) ;
+
+ // To avoid echo frame lag, compute sensorToVworld based on
+ // computed trackerToVworld. getSensorToVworld() isn't current
+ // for this frame.
+ Sensor s = e.getSensor() ;
+ s.getRead(sensorToTracker) ;
+ sensorToVworld.mul(trackerToVworld, sensorToTracker) ;
+
+ // Get the scale center.
+ if (transformCenterSource == HOTSPOT) {
+ s.getHotspot(center) ;
+ sensorToVworld.transform(center) ;
+ }
+ else {
+ center.set(transformCenter) ;
+ }
+
+ // Apply the scale to the view platform.
+ t3d.set(scale) ;
+ transformAboutCenter(viewPlatformToVworld, center, t3d) ;
+
+ // Incremental scaling at the extremes can lead to numerical
+ // instability, so catch BadTransformException to prevent the
+ // behavior thread from being killed. Using a cumulative scale
+ // matrix avoids this problem to a better extent, but causes the
+ // 6DOF sensor hotspot center to jitter excessively.
+ try {
+ targetTG.setTransform(viewPlatformToVworld) ;
+ }
+ catch (BadTransformException bt) {
+ conditionViewScale(viewPlatformToVworld) ;
+ }
+
+ // Apply the scale to trackerToVworld.
+ transformAboutCenter(trackerToVworld, center, t3d) ;
+
+ if (readAction6D == ECHO) {
+ // Scale sensor echo to compensate for the new view
+ // platform scale.
+ transformAboutCenter(sensorToVworld, center, t3d) ;
+ updateEcho(s, sensorToVworld) ;
+ }
+ }
+ }
+
+ /**
+ * Implements a 6DOF sensor read listener that updates the orientation and
+ * position of the sensor's echo in the virtual world.
+ *
+ * @see #setEchoType
+ * @see #setEchoSize
+ * @see #setReadAction6D
+ * @see SensorGnomonEcho
+ * @see SensorBeamEcho
+ */
+ public class EchoReadListener6D implements SensorReadListener {
+ private Transform3D sensorToVworld = new Transform3D() ;
+
+ public void read(SensorEvent e) {
+ Sensor s = e.getSensor() ;
+ view.getSensorToVworld(s, sensorToVworld) ;
+ updateEcho(s, sensorToVworld) ;
+ }
+ }
+
+ /**
+ * Implements a 2D valuator listener that rotates the view platform. The
+ * X and Y values from the valuator should have a continuous range from
+ * -1.0 to +1.0, although the rotation speed can be scaled to compensate
+ * for a different range. The X and Y values are found in the sensor's
+ * read matrix at the indices specified by
+ * <code>setMatrixIndices2D</code>, with defaults of 3 and 7 respectively.
+ * <p>
+ * The rotation direction is controlled by the direction the 2D valuator
+ * is pushed, and the rotation speed is scaled by the magnitude of the 2D
+ * valuator read values.
+ * <p>
+ * This listener will work in conjunction with a 6DOF sensor if supplied
+ * in the constructor. If a 6DOF sensor is provided and
+ * <code>setRotationCoords</code> has been called with the value
+ * <code>SENSOR</code>, then the rotation is applied in the 6DOF sensor's
+ * coordinate system; otherwise the rotation is applied either in head
+ * coordinates or in view platform coordinates. If a 6DOF sensor is
+ * provided and <code>setTransformCenterSource</code> has been called with
+ * the value <code>HOTSPOT</code>, then rotation is about the 6DOF
+ * sensor's hotspot; otherwise, the rotation center is the value set by
+ * <code>setTransformCenter</code>.
+ *
+ * @see #setReadAction2D
+ * @see #setButtonAction2D
+ * @see #setRotationCoords
+ * @see #setTransformCenterSource
+ * @see #setTransformCenter
+ * @see #setRotationSpeed
+ * @see #setThreshold2D
+ * @see #setMatrixIndices2D
+ */
+ public class RotationListener2D extends ListenerBase {
+ private Sensor sensor2D, sensor6D ;
+ private double[] m = new double[16] ;
+ private Vector3d axis = new Vector3d() ;
+ private Point3d center = new Point3d() ;
+ private Transform3D t3d = new Transform3D() ;
+ private AxisAngle4d aa4d = new AxisAngle4d() ;
+ private Transform3D sensor2DRead = new Transform3D() ;
+ private Transform3D headToVworld = new Transform3D() ;
+ private double speedScaled ;
+
+ protected void initAction(Sensor s) {
+ super.initAction(s) ;
+ if (rotationCoords == HEAD) {
+ view.setUserHeadToVworldEnable(true) ;
+ }
+ if (s != null && readAction6D == ECHO) {
+ // Disable the 6DOF echo. It will be updated in this action.
+ eventAgent.removeSensorReadListener(s, echoReadListener6D) ;
+ }
+ }
+
+ protected void endAction(Sensor s) {
+ super.endAction(s) ;
+ viewPlatformToVworld.normalize() ;
+ targetTG.setTransform(viewPlatformToVworld) ;
+ if (rotationCoords == HEAD) {
+ view.setUserHeadToVworldEnable(false) ;
+ }
+ if (s != null && readAction6D == ECHO) {
+ eventAgent.addSensorReadListener(s, echoReadListener6D) ;
+ }
+ }
+
+ /**
+ * Construct an instance of this class with the specified sensors.
+ *
+ * @param sensor2D the 2D valuator whose X and Y values drive the
+ * rotation
+ * @param sensor6D the 6DOF sensor to use if the rotation coordinate
+ * system is set to <code>SENSOR</code> or the rotation center source
+ * is <code>HOTSPOT</code>; may be <code>null</code>
+ */
+ public RotationListener2D(Sensor sensor2D, Sensor sensor6D) {
+ this.sensor2D = sensor2D ;
+ this.sensor6D = sensor6D ;
+
+ if (rotationUnits == DEGREES)
+ speedScaled = rotationSpeed * Math.PI / 180.0 ;
+ else
+ speedScaled = rotationSpeed ;
+ }
+
+ public void read(SensorEvent e) {
+ sensor2D.getRead(sensor2DRead) ;
+ sensor2DRead.get(m) ;
+
+ if (m[x2D] > threshold2D || m[x2D] < -threshold2D ||
+ m[y2D] > threshold2D || m[y2D] < -threshold2D) {
+ // Initialize action on threshold crossing.
+ if (!isActive()) initAction(sensor6D) ;
+
+ // m[x2D] is the X valuator value and m[y2D] is the Y valuator
+ // value. Use these to construct the rotation axis.
+ double length = Math.sqrt(m[x2D]*m[x2D] + m[y2D]*m[y2D]) ;
+ double iLength = 1.0/length ;
+ axis.set(m[y2D]*iLength, -m[x2D]*iLength, 0.0) ;
+
+ if (sensor6D != null) {
+ // To avoid echo frame lag, compute sensorToVworld based
+ // on computed trackerToVworld. getSensorToVworld() isn't
+ // current for this frame.
+ sensor6D.getRead(sensorToTracker) ;
+ sensorToVworld.mul(trackerToVworld, sensorToTracker) ;
+ }
+
+ // Transform rotation axis into target coordinate system.
+ if (sensor6D != null && rotationCoords == SENSOR) {
+ if (nominalSensorRotation != null)
+ nominalSensorRotation.transform(axis) ;
+
+ sensorToVworld.transform(axis) ;
+ }
+ else if (rotationCoords == HEAD) {
+ view.getUserHeadToVworld(headToVworld) ;
+ headToVworld.transform(axis) ;
+ }
+ else {
+ viewPlatformToVworld.transform(axis) ;
+ }
+
+ // Get the rotation center.
+ if (transformCenterSource == HOTSPOT && sensor6D != null) {
+ sensor6D.getHotspot(center) ;
+ sensorToVworld.transform(center) ;
+ }
+ else {
+ center.set(transformCenter) ;
+ }
+
+ double frameTime = 1.0 ;
+ if (rotationTimeBase == PER_SECOND)
+ frameTime = (e.getTime() - e.getLastTime()) / 1e9 ;
+
+ // Construct origin-based rotation about axis.
+ aa4d.set(axis, speedScaled * frameTime * length) ;
+ t3d.set(aa4d) ;
+
+ // Apply the rotation to the view platform.
+ transformAboutCenter(viewPlatformToVworld, center, t3d) ;
+ targetTG.setTransform(viewPlatformToVworld) ;
+
+ if (sensor6D != null) {
+ // Apply the rotation to trackerToVworld.
+ transformAboutCenter(trackerToVworld, center, t3d) ;
+ }
+
+ if (sensor6D != null && readAction6D == ECHO) {
+ // Transform sensor echo to compensate for the new view
+ // platform movement.
+ transformAboutCenter(sensorToVworld, center, t3d) ;
+ updateEcho(sensor6D, sensorToVworld) ;
+ }
+ }
+ else {
+ // Initialize action on next threshold crossing.
+ if (isActive()) endAction(sensor6D) ;
+ }
+ }
+
+ public void pressed(SensorEvent e) {
+ initAction(sensor6D) ;
+ }
+
+ public void released(SensorEvent e) {
+ if (isActive()) endAction(sensor6D) ;
+ }
+
+ public void dragged(SensorEvent e) {
+ read(e) ;
+ }
+ }
+
+ /**
+ * Implements a 2D valuator listener that translates the view platform.
+ * The X and Y values from the valuator should have a continuous range
+ * from -1.0 to +1.0, although the translation speed can be scaled to
+ * compensate for a different range. The X and Y values are found in the
+ * sensor's read matrix at the indices specified by
+ * <code>setMatrixIndices2D</code>, with defaults of 3 and 7 respectively.
+ * <p>
+ * The translation direction is controlled by the direction the 2D
+ * valuator is pushed, and the speed is the translation speed scaled by
+ * the fast speed factor and the magnitude of the 2D valuator reads.
+ * <p>
+ * This listener will work in conjunction with a 6DOF sensor if supplied
+ * in the constructor. If a 6DOF sensor is provided then the translation
+ * occurs along the basis vectors of the 6DOF sensor's coordinate system;
+ * otherwise, the translation occurs along the view platform's basis
+ * vectors.
+ *
+ * @see #setReadAction2D
+ * @see #setButtonAction2D
+ * @see #setTranslationSpeed
+ * @see #setFastSpeedFactor
+ * @see #setThreshold2D
+ * @see #setMatrixIndices2D
+ */
+ public class TranslationListener2D extends ListenerBase {
+ private Sensor sensor2D, sensor6D ;
+ private double[] m = new double[16] ;
+ private Vector3d v3d = new Vector3d() ;
+ private Transform3D sensor2DRead = new Transform3D() ;
+ private double speedScaled ;
+
+ protected void initAction(Sensor s) {
+ super.initAction(s) ;
+ if (s != null && readAction6D == ECHO) {
+ // Disable the 6DOF echo. It will be updated in this action.
+ eventAgent.removeSensorReadListener(s, echoReadListener6D) ;
+ }
+ }
+
+ protected void endAction(Sensor s) {
+ super.endAction(s) ;
+ if (s != null && readAction6D == ECHO) {
+ // Enable the 6DOF sensor echo.
+ eventAgent.addSensorReadListener(s, echoReadListener6D) ;
+ }
+ }
+
+ /**
+ * Construct an instance of this class using the specified sensors.
+ *
+ * @param sensor2D 2D valuator sensor for translation
+ * @param sensor6D 6DOF sensor for translation direction; may be
+ * <code>null</code>
+ */
+ public TranslationListener2D(Sensor sensor2D, Sensor sensor6D) {
+ this.sensor2D = sensor2D ;
+ this.sensor6D = sensor6D ;
+
+ // Apply virtual to physical scale if needed.
+ if (translationUnits == VIRTUAL_UNITS)
+ speedScaled = translationSpeed *
+ fastSpeedFactor / getPhysicalToVirtualScale() ;
+ else
+ speedScaled = translationSpeed * fastSpeedFactor ;
+
+ // Apply physical to view platform scale if needed.
+ if (sensor6D == null)
+ speedScaled *= getPhysicalToViewPlatformScale() ;
+ }
+
+ public void read(SensorEvent e) {
+ sensor2D.getRead(sensor2DRead) ;
+ sensor2DRead.get(m) ;
+
+ if (m[x2D] > threshold2D || m[x2D] < -threshold2D ||
+ m[y2D] > threshold2D || m[y2D] < -threshold2D) {
+ // Initialize action on threshold crossing.
+ if (!isActive()) initAction(sensor6D) ;
+
+ // m[x2D] is the X valuator value and m[y2D] is the Y valuator
+ // value. Use these to construct the translation vector.
+ double length = Math.sqrt(m[x2D]*m[x2D] + m[y2D]*m[y2D]) ;
+ double iLength = 1.0/length ;
+ v3d.set(m[x2D]*iLength, 0.0, -m[y2D]*iLength) ;
+
+ // Transform translation vector into target coordinate system.
+ if (sensor6D != null) {
+ if (nominalSensorRotation != null)
+ nominalSensorRotation.transform(v3d) ;
+
+ // To avoid echo frame lag, compute sensorToVworld based
+ // on computed trackerToVworld. getSensorToVworld() isn't
+ // current for this frame.
+ sensor6D.getRead(sensorToTracker) ;
+ sensorToVworld.mul(trackerToVworld, sensorToTracker) ;
+ sensorToVworld.transform(v3d) ;
+ }
+ else {
+ viewPlatformToVworld.transform(v3d) ;
+ }
+
+ double frameTime = 1.0 ;
+ if (translationTimeBase == PER_SECOND)
+ frameTime = (e.getTime() - e.getLastTime()) / 1e9 ;
+
+ v3d.scale(frameTime * speedScaled * length) ;
+
+ // Translate the view platform.
+ translateTransform(viewPlatformToVworld, v3d) ;
+ targetTG.setTransform(viewPlatformToVworld) ;
+
+ if (sensor6D != null) {
+ // Apply the translation to trackerToVworld.
+ translateTransform(trackerToVworld, v3d) ;
+ }
+
+ if (sensor6D != null && readAction6D == ECHO) {
+ // Translate sensor echo to compensate for the new view
+ // platform movement.
+ translateTransform(sensorToVworld, v3d) ;
+ updateEcho(sensor6D, sensorToVworld) ;
+ }
+ }
+ else {
+ // Initialize action on next threshold crossing.
+ if (isActive()) endAction(sensor6D) ;
+ }
+ }
+
+ public void pressed(SensorEvent e) {
+ initAction(sensor6D) ;
+ }
+
+ public void released(SensorEvent e) {
+ if (isActive()) endAction(sensor6D) ;
+ }
+
+ public void dragged(SensorEvent e) {
+ read(e) ;
+ }
+ }
+
+ /**
+ * Implements a 2D valuator listener that scales the view platform.
+ * Pushing the valuator forwards gives the appearance of the virtual world
+ * increasing in size, while pushing the valuator backwards makes the
+ * virtual world appear to shrink. The X and Y values from the valuator
+ * should have a continuous range from -1.0 to +1.0, although the scale
+ * speed can be adjusted to compensate for a different range.
+ * <p>
+ * This listener will work in conjunction with a 6DOF sensor if supplied
+ * in the constructor. If <code>setTransformCenterSource</code> has been
+ * called with the value <code>HOTSPOT</code>, then scaling is about the
+ * 6DOF sensor's hotspot; otherwise, the scaling center is the value set
+ * by <code>setTransformCenter</code>.
+ *
+ * @see #setReadAction2D
+ * @see #setButtonAction2D
+ * @see #setScaleSpeed
+ * @see #setTransformCenter
+ * @see #setThreshold2D
+ * @see #setMatrixIndices2D
+ */
+ public class ScaleListener2D extends ListenerBase {
+ private Sensor sensor2D, sensor6D ;
+ private double[] m = new double[16] ;
+ private Point3d center = new Point3d() ;
+ private Transform3D t3d = new Transform3D() ;
+ private Transform3D sensor2DRead = new Transform3D() ;
+
+ protected void initAction(Sensor s) {
+ super.initAction(s) ;
+ if (s != null && readAction6D == ECHO) {
+ // Disable the 6DOF echo. It will be updated in this action.
+ eventAgent.removeSensorReadListener(s, echoReadListener6D) ;
+ }
+ }
+
+ protected void endAction(Sensor s) {
+ super.endAction(s) ;
+ conditionViewScale(viewPlatformToVworld) ;
+ if (s != null && readAction6D == ECHO) {
+ // Enable the 6DOF sensor echo.
+ eventAgent.addSensorReadListener(s, echoReadListener6D) ;
+ }
+ }
+
+ /**
+ * Construct an instance of this class with the specified sensors.
+ *
+ * @param sensor2D the 2D valuator whose Y value drive the scaling
+ * @param sensor6D the 6DOF sensor to use if the rotation/scale center
+ * source is <code>HOTSPOT</code>; may be <code>null</code>
+ */
+ public ScaleListener2D(Sensor sensor2D, Sensor sensor6D) {
+ this.sensor2D = sensor2D ;
+ this.sensor6D = sensor6D ;
+ }
+
+ public void read(SensorEvent e) {
+ sensor2D.getRead(sensor2DRead) ;
+ sensor2DRead.get(m) ;
+
+ if (m[y2D] > threshold2D || m[y2D] < -threshold2D) {
+ // Initialize action on threshold crossing.
+ if (!isActive()) initAction(sensor6D) ;
+
+ if (sensor6D != null) {
+ // To avoid echo frame lag, compute sensorToVworld based
+ // on computed trackerToVworld. getSensorToVworld() isn't
+ // current for this frame.
+ sensor6D.getRead(sensorToTracker) ;
+ sensorToVworld.mul(trackerToVworld, sensorToTracker) ;
+ }
+
+ // Get the scale center.
+ if (sensor6D != null && transformCenterSource == HOTSPOT) {
+ sensor6D.getHotspot(center) ;
+ sensorToVworld.transform(center) ;
+ }
+ else {
+ center.set(transformCenter) ;
+ }
+
+ // Compute incremental scale for this frame.
+ double frameTime = 1.0 ;
+ if (scaleTimeBase == PER_SECOND)
+ frameTime = (e.getTime() - e.getLastTime()) / 1e9 ;
+
+ // Map range: [-1.0 .. 0 .. 1.0] to:
+ // [scaleSpeed**frameTime .. 1 .. 1.0/(scaleSpeed**frameTime)]
+ double scale = Math.pow(scaleSpeed, (-m[y2D]*frameTime)) ;
+
+ // Apply the scale to the view platform.
+ t3d.set(scale) ;
+ transformAboutCenter(viewPlatformToVworld, center, t3d) ;
+
+ // Incremental scaling at the extremes can lead to numerical
+ // instability, so catch BadTransformException to prevent the
+ // behavior thread from being killed. Using a cumulative
+ // scale matrix avoids this problem to a better extent, but
+ // causes the 6DOF sensor hotspot center to jitter
+ // excessively.
+ try {
+ targetTG.setTransform(viewPlatformToVworld) ;
+ }
+ catch (BadTransformException bt) {
+ conditionViewScale(viewPlatformToVworld) ;
+ }
+
+ if (sensor6D != null) {
+ // Apply the scale to trackerToVworld.
+ transformAboutCenter(trackerToVworld, center, t3d) ;
+ }
+
+ if (sensor6D != null && readAction6D == ECHO) {
+ // Scale sensor echo to compensate for the new view
+ // platform scale.
+ transformAboutCenter(sensorToVworld, center, t3d) ;
+ updateEcho(sensor6D, sensorToVworld) ;
+ }
+ }
+ else {
+ // Initialize action on next threshold crossing.
+ if (isActive()) endAction(sensor6D) ;
+ }
+ }
+
+ public void pressed(SensorEvent e) {
+ initAction(sensor6D) ;
+ }
+
+ public void released(SensorEvent e) {
+ if (isActive()) endAction(sensor6D) ;
+ }
+
+ public void dragged(SensorEvent e) {
+ read(e) ;
+ }
+ }
+
+ /**
+ * Resets the view back to the home transform when a specified number of
+ * buttons are down simultaneously.
+ *
+ * @see #setResetViewButtonCount6D
+ * @see ViewPlatformBehavior#setHomeTransform
+ * ViewPlatformBehavior.setHomeTransform
+ */
+ public class ResetViewListener extends SensorInputAdaptor {
+ private int resetCount ;
+ private int[] buttonState = null ;
+ private boolean goHomeNextRead = false ;
+
+ /**
+ * Creates a sensor listener that resets the view when the specified
+ * number of buttons are down simultaneously.
+ *
+ * @param s the sensor to listen to
+ * @param count the number of buttons that must be down simultaneously
+ */
+ public ResetViewListener(Sensor s, int count) {
+ resetCount = count ;
+ buttonState = new int[s.getSensorButtonCount()] ;
+ }
+
+ public void pressed(SensorEvent e) {
+ int count = 0 ;
+ e.getButtonState(buttonState) ;
+ for (int i = 0 ; i < buttonState.length ; i++)
+ if (buttonState[i] == 1) count++ ;
+
+ if (count >= resetCount)
+ // Ineffectual to reset immediately while other listeners may
+ // be setting the view transform.
+ goHomeNextRead = true ;
+ }
+
+ public void read(SensorEvent e) {
+ if (goHomeNextRead) {
+ goHome() ;
+ goHomeNextRead = false ;
+ }
+ }
+ }
+
+ /**
+ * Updates the echo position and orientation. The echo is placed at the
+ * sensor hotspot position if applicable. This implementation assumes the
+ * hotspot position and orientation have been incorporated into the echo
+ * geometry.
+ *
+ * @param sensor the sensor to be echoed
+ * @param sensorToVworld transform from sensor coordinates to virtual
+ * world coordinates
+ * @see #setEchoType
+ * @see #setEchoSize
+ * @see #setReadAction6D
+ * @see SensorGnomonEcho
+ * @see SensorBeamEcho
+ */
+ protected void updateEcho(Sensor sensor, Transform3D sensorToVworld) {
+ echoTransformGroup.setTransform(sensorToVworld) ;
+ }
+
+ /**
+ * Property which sets a 6DOF sensor for manipulating the view platform.
+ * This sensor must generate 6 degree of freedom orientation and position
+ * data in physical meters relative to the sensor's tracker base.
+ * <p>
+ * This property is set in the configuration file. The first command form
+ * assumes that a <code>ViewingPlatform</code> is being used and that the
+ * sensor name can be looked up from a <code>ConfiguredUniverse</code>
+ * reference retrieved from <code>ViewingPlatform.getUniverse</code>. The
+ * second form is preferred and accepts the Sensor reference directly.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * Sensor6D <i>&lt;sensorName&gt;</i>)
+ * <p>
+ * <b>Alternative Syntax:</b><br>(ViewPlatformBehaviorProperty
+ * <i>&lt;name&gt;</i> Sensor6D (Sensor <i>&lt;sensorName&gt;</i>))
+ *
+ * @param sensor array of length 1 containing a <code>String</code> or
+ * a <code>Sensor</code>
+ */
+ public void Sensor6D(Object[] sensor) {
+ if (sensor.length != 1)
+ throw new IllegalArgumentException
+ ("Sensor6D requires a single name or Sensor instance") ;
+
+ if (sensor[0] instanceof String)
+ sensor6DName = (String)sensor[0] ;
+ else if (sensor[0] instanceof Sensor)
+ sensor6D = (Sensor)sensor[0] ;
+ else
+ throw new IllegalArgumentException
+ ("Sensor6D must be a name or a Sensor instance") ;
+ }
+
+ /**
+ * Returns a reference to the 6DOF sensor used for manipulating the view
+ * platform.
+ *
+ * @return the 6DOF sensor
+ */
+ public Sensor getSensor6D() {
+ return sensor6D ;
+ }
+
+ /**
+ * Property which sets a 2D sensor for manipulating the view platform.
+ * This is intended to support devices which incorporate a separate 2D
+ * valuator along with the 6DOF sensor. The X and Y values from the
+ * valuator should have a continuous range from -1.0 to +1.0, although
+ * rotation, translation, and scaling speeds can be scaled to compensate
+ * for a different range. The X and Y values are found in the sensor's
+ * read matrix at the indices specified by the
+ * <code>MatrixIndices2D</code> property, with defaults of 3 and 7
+ * (the X and Y translation components) respectively.
+ * <p>
+ * This property is set in the configuration file. The first command form
+ * assumes that a <code>ViewingPlatform</code> is being used and that the
+ * sensor name can be looked up from a <code>ConfiguredUniverse</code>
+ * reference retrieved from <code>ViewingPlatform.getUniverse</code>. The
+ * second form is preferred and accepts the Sensor reference directly.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * Sensor2D <i>&lt;sensorName&gt;</i>)
+ * <p>
+ * <b>Alternative Syntax:</b><br>(ViewPlatformBehaviorProperty
+ * <i>&lt;name&gt;</i> Sensor2D (Sensor <i>&lt;sensorName&gt;</i>))
+ *
+ * @param sensor array of length 1 containing a <code>String</code> or
+ * a <code>Sensor</code>
+ */
+ public void Sensor2D(Object[] sensor) {
+ if (sensor.length != 1)
+ throw new IllegalArgumentException
+ ("Sensor2D requires a single name or Sensor instance") ;
+
+ if (sensor[0] instanceof String)
+ sensor2DName = (String)sensor[0] ;
+ else if (sensor[0] instanceof Sensor)
+ sensor2D = (Sensor)sensor[0] ;
+ else
+ throw new IllegalArgumentException
+ ("Sensor2D must be a name or a Sensor instance") ;
+ }
+
+ /**
+ * Returns a reference to the 2D valuator used for manipulating the view
+ * platform.
+ *
+ * @return the 2D valuator
+ */
+ public Sensor getSensor2D() {
+ return sensor2D ;
+ }
+
+ /**
+ * Property which sets a button action for the 6DOF sensor. The choices
+ * are <code>TranslateForward</code>, <code>TranslateBackward</code>,
+ * <code>GrabView</code>, <code>RotateCCW</code>, <code>RotateCW</code>,
+ * <code>ScaleUp</code>, <code>ScaleDown</code>, or <code>None</code>. By
+ * default, button 0 is bound to <code>GrabView</code>, button 1 is bound
+ * to <code>TranslateForward</code>, and button 2 is bound to
+ * <code>TranslateBackward</code>. If there are fewer than three buttons
+ * available, then the default button actions with the lower button
+ * indices have precedence. A value of <code>None</code> indicates that
+ * no default action is to be associated with the specified button.
+ * <p>
+ * <code>TranslateForward</code> moves the view platform forward along the
+ * direction the sensor is pointing. <code>TranslateBackward</code> does
+ * the same, in the opposite direction. <code>GrabView</code> directly
+ * manipulates the view by moving it in inverse response to the sensor's
+ * position and orientation. <code>RotateCCW</code> and
+ * <code>RotateCW</code> rotate about a Y axis, while <code>ScaleUp</code>
+ * and <code>ScaleDown</code> scale the view platform larger and smaller.
+ * <p>
+ * Specifying a button index that is greater than that available with the
+ * 6DOF sensor will result in an <code>ArrayOutOfBoundsException</code>
+ * when the behavior is initialized or attached to a
+ * <code>ViewingPlatform</code>.
+ * <p>
+ * This property is set in the configuration file read by
+ * <code>ConfiguredUniverse</code>.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * ButtonAction6D <i>&lt;button index&gt;</i>
+ * [GrabView | TranslateForward | TranslateBackward | RotateCCW |
+ * RotateCW | ScaleUp | ScaleDown | None])
+ *
+ * @param action array of length 2 containing a <code>Double</code> and a
+ * <code>String</code>.
+ * @see #setButtonAction6D
+ * @see #Sensor6D Sensor6D
+ * @see #TranslationSpeed TranslationSpeed
+ * @see #AccelerationTime AccelerationTime
+ * @see #ConstantSpeedTime ConstantSpeedTime
+ * @see #FastSpeedFactor FastSpeedFactor
+ * @see #RotationSpeed RotationSpeed
+ * @see #RotationCoords RotationCoords
+ * @see #ScaleSpeed ScaleSpeed
+ * @see #TransformCenterSource TransformCenterSource
+ * @see #TransformCenter TransformCenter
+ * @see GrabViewListener6D
+ * @see TranslationListener6D
+ * @see RotationListener6D
+ * @see ScaleListener6D
+ */
+ public void ButtonAction6D(Object[] action) {
+ if (! (action.length == 2 &&
+ action[0] instanceof Double && action[1] instanceof String))
+ throw new IllegalArgumentException
+ ("\nButtonAction6D must be a number and a string") ;
+
+ int button = ((Double)action[0]).intValue() ;
+ String actionString = (String)action[1] ;
+
+ if (actionString.equals("GrabView"))
+ setButtonAction6D(button, GRAB_VIEW) ;
+ else if (actionString.equals("TranslateForward"))
+ setButtonAction6D(button, TRANSLATE_FORWARD) ;
+ else if (actionString.equals("TranslateBackward"))
+ setButtonAction6D(button, TRANSLATE_BACKWARD) ;
+ else if (actionString.equals("RotateCCW"))
+ setButtonAction6D(button, ROTATE_CCW) ;
+ else if (actionString.equals("RotateCW"))
+ setButtonAction6D(button, ROTATE_CW) ;
+ else if (actionString.equals("ScaleUp"))
+ setButtonAction6D(button, SCALE_UP) ;
+ else if (actionString.equals("ScaleDown"))
+ setButtonAction6D(button, SCALE_DOWN) ;
+ else if (actionString.equals("None"))
+ setButtonAction6D(button, NONE) ;
+ else
+ throw new IllegalArgumentException
+ ("\nButtonAction6D must be GrabView, TranslateForward, " +
+ "TranslateBackward, RotateCCW, RotateCW, ScaleUp, " +
+ "ScaleDown, or None") ;
+ }
+
+ /**
+ * Sets a button action for the 6DOF sensor. The choices are
+ * <code>TRANSLATE_FORWARD</code>, <code>TRANSLATE_BACKWARD</code>,
+ * <code>GRAB_VIEW</code>, <code>ROTATE_CCW</code>,
+ * <code>ROTATE_CW</code>, <code>SCALE_UP</code>, <code>SCALE_DOWN</code>,
+ * or <code>NONE</code>. By default, button 0 is bound to
+ * <code>GRAB_VIEW</code>, button 1 is bound to
+ * <code>TRANSLATE_FORWARD</code>, and button 2 is bound to
+ * <code>TRANSLATE_BACKWARD</code>. If there are fewer than three buttons
+ * available, then the default button actions with the lower button
+ * indices have precedence. A value of <code>NONE</code> indicates that
+ * no default action is to be associated with the specified button.
+ * <p>
+ * <code>TRANSLATE_FORWARD</code> moves the view platform forward along
+ * the direction the sensor is pointing. <code>TRANSLATE_BACKWARD</code>
+ * does the same, in the opposite direction. <code>GRAB_VIEW</code>
+ * directly manipulates the view by moving it in inverse response to the
+ * sensor's position and orientation. <code>ROTATE_CCW</code> and
+ * <code>ROTATE_CW</code> rotate about a Y axis, while
+ * <code>SCALE_UP</code> and <code>SCALE_DOWN</code> scale the view
+ * platform larger and smaller.
+ * <p>
+ * Specifying a button index that is greater that that available with the
+ * 6DOF sensor will result in an <code>ArrayOutOfBoundsException</code>
+ * when the behavior is initialized or attached to a
+ * <code>ViewingPlatform</code>.
+ * <p>
+ * This method only configures the button listeners pre-defined by
+ * this behavior. For complete control over the button actions, access
+ * the <code>SensorEventAgent</code> used by this behavior directly.
+ *
+ * @param button index of the button to bind
+ * @param action either <code>TRANSLATE_FORWARD</code>,
+ * <code>TRANSLATE_BACKWARD</code>, <code>GRAB_VIEW</code>,
+ * <code>ROTATE_CCW</code>, <code>ROTATE_CW<code>, </code>SCALE_UP</code>,
+ * <code>SCALE_DOWN</code>, or <code>NONE</code>
+ * @see #setTranslationSpeed
+ * @see #setAccelerationTime
+ * @see #setConstantSpeedTime
+ * @see #setFastSpeedFactor
+ * @see #setRotationSpeed
+ * @see #setRotationCoords
+ * @see #setScaleSpeed
+ * @see #setTransformCenterSource
+ * @see #setTransformCenter
+ * @see #getSensorEventAgent
+ * @see GrabViewListener6D
+ * @see TranslationListener6D
+ * @see RotationListener6D
+ * @see ScaleListener6D
+ */
+ public synchronized void setButtonAction6D(int button, int action) {
+ if (! (action == TRANSLATE_FORWARD || action == TRANSLATE_BACKWARD ||
+ action == GRAB_VIEW || action == ROTATE_CCW ||
+ action == ROTATE_CW || action == SCALE_UP ||
+ action == SCALE_DOWN || action == NONE))
+ throw new IllegalArgumentException
+ ("\naction must be TRANSLATE_FORWARD, TRANSLATE_BACKWARD, " +
+ "GRAB_VIEW, ROTATE_CCW, ROTATE_CW, SCALE_UP, SCALE_DOWN, " +
+ "or NONE") ;
+
+ while (button >= buttonActions6D.size()) {
+ buttonActions6D.add(null) ;
+ }
+ buttonActions6D.set(button, new Integer(action)) ;
+ }
+
+
+ /**
+ * Gets the action associated with the specified button on the 6DOF sensor.
+ *
+ * @return the action associated with the button
+ */
+ public int getButtonAction6D(int button) {
+ if (button >= buttonActions6D.size())
+ return NONE ;
+
+ Integer i = (Integer)buttonActions6D.get(button) ;
+ if (i == null)
+ return NONE ;
+ else
+ return i.intValue() ;
+ }
+
+ /**
+ * Property which sets the action to be bound to 2D valuator reads. This
+ * action will be performed on each frame whenever no button actions have
+ * been invoked and the valuator read value is greater than the threshold
+ * range specified by the <code>Threshold2D</code> property.
+ * <p>
+ * The X and Y values from the valuator should have a continuous range
+ * from -1.0 to +1.0, although speeds can be scaled to compensate for a
+ * different range. The X and Y values are found in the sensor's read
+ * matrix at the indices specified by <code>MatrixIndices2D</code>, with
+ * defaults of 3 and 7 respectively.
+ * <p>
+ * The default property value of <code>Rotation</code> rotates the view
+ * platform in the direction the valuator is pushed. The rotation
+ * coordinate system is set by the <code>RotationCoords</code> property,
+ * with a default of <code>Sensor</code>. The rotation occurs about a
+ * point in the virtual world set by the
+ * <code>TransformCenterSource</code> and <code>TransformCenter</code>
+ * properties, with the default set to rotate about the hotspot of a 6DOF
+ * sensor, if one is available, or about the origin of the virtual world
+ * if not. The rotation speed is scaled by the valuator read value up to
+ * the maximum speed set with the <code>RotationSpeed</code> property; the
+ * default is 180 degrees per second.
+ * <p>
+ * A property value of <code>Translation</code> moves the view platform in
+ * the direction the valuator is pushed. The translation occurs along the
+ * X and Z basis vectors of either a 6DOF sensor or the view platform if a
+ * 6DOF sensor is not specified. The translation speed is scaled by the
+ * valuator read value up to a maximum set by the product of the
+ * <code>TranslationSpeed</code> and <code>FastSpeedFactor</code> property
+ * values.
+ * <p>
+ * If this property value is to <code>Scale</code>, then the view platform
+ * is scaled smaller or larger when the valuator is pushed forward or
+ * backward. The scaling occurs about a point in the virtual world set by
+ * the <code>TransformCenterSource</code> and <code>TransformCenter</code>
+ * properties. The scaling speed is set with the <code>ScaleSpeed</code>
+ * property, with a default scale factor of 2.0 per second at the extreme
+ * negative range of -1.0, and a factor of 0.5 per second at the extreme
+ * positive range of +1.0.
+ * <p>
+ * A value of <code>None</code> prevents <code>Rotation</code> from being
+ * bound to the 2D valuator reads.
+ * <p>
+ * This property is set in the configuration file read by
+ * <code>ConfiguredUniverse</code>.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * ReadAction2D [Rotation | Translation | Scale | None])
+ *
+ * @param action array of length 1 containing a <code>String</code>
+ * @see #setReadAction2D
+ * @see #RotationCoords RotationCoords
+ * @see #RotationSpeed RotationSpeed
+ * @see #TransformCenterSource TransformCenterSource
+ * @see #TransformCenter TransformCenter
+ * @see #TranslationSpeed TranslationSpeed
+ * @see #FastSpeedFactor FastSpeedFactor
+ * @see #ScaleSpeed ScaleSpeed
+ * @see #MatrixIndices2D MatrixIndices2D
+ * @see RotationListener2D
+ * @see TranslationListener2D
+ * @see ScaleListener2D
+ */
+ public void ReadAction2D(Object[] action) {
+ if (! (action.length == 1 && action[0] instanceof String))
+ throw new IllegalArgumentException
+ ("\nReadAction2D must be a String") ;
+
+ String actionString = (String)action[0] ;
+
+ if (actionString.equals("Rotation"))
+ setReadAction2D(ROTATION) ;
+ else if (actionString.equals("Translation"))
+ setReadAction2D(TRANSLATION) ;
+ else if (actionString.equals("Scale"))
+ setReadAction2D(SCALE) ;
+ else if (actionString.equals("None"))
+ setReadAction2D(NONE) ;
+ else
+ throw new IllegalArgumentException
+ ("\nReadAction2D must be Rotation, Translation, Scale, " +
+ "or None") ;
+ }
+
+ /**
+ * Sets the action to be bound to 2D valuator reads. This action will be
+ * performed on each frame whenever no button actions have been invoked
+ * and the valuator read value is greater than the threshold range
+ * specified by <code>setThreshold2D</code>.
+ * <p>
+ * The X and Y values from the valuator should have a continuous range
+ * from -1.0 to +1.0, although speeds can be scaled to compensate for a
+ * different range. The X and Y values are found in the sensor's read
+ * matrix at the indices specified by the <code>setMatrixIndices2D</code>
+ * method, with defaults of 3 and 7 respectively.
+ * <p>
+ * The default action of <code>ROTATION</code> rotates the view platform
+ * in the direction the valuator is pushed. The rotation coordinate
+ * system is set by <code>setRotationCoords</code>, with a default of
+ * <code>SENSOR</code>. The rotation occurs about a point in the virtual
+ * world set by <code>setTransformCenterSource</code> and
+ * <code>setTransformCenter</code>, with the default set to rotate about
+ * the hotspot of a 6DOF sensor, if one is available, or about the origin
+ * of the virtual world if not. The rotation speed is scaled by the
+ * valuator read value up to the maximum speed set with
+ * <code>setRotationSpeed</code>; the default is 180 degrees per second.
+ * <p>
+ * A value of <code>TRANSLATION</code> moves the view platform in the
+ * direction the valuator is pushed. The translation occurs along the X
+ * and Z basis vectors of either a 6DOF sensor or the view platform if a
+ * 6DOF sensor is not specified. The translation speed is scaled by the
+ * valuator read value up to a maximum set by the product of the
+ * <code>setTranslationSpeed</code> and <code>setFastSpeedFactor</code>
+ * values.
+ * <p>
+ * If the value is to <code>SCALE</code>, then the view platform is scaled
+ * smaller or larger when the valuator is pushed forward or backward. The
+ * scaling occurs about a point in the virtual world set by
+ * <code>setTransformCenterSource</code> and
+ * <code>setTransformCenter</code>. The scaling speed is set with
+ * <code>setScaleSpeed</code>, with a default scale factor of 2.0 per
+ * second at the extreme negative range of -1.0, and a factor of 0.5 per
+ * second at the extreme positive range of +1.0.
+ * <p>
+ * A value of <code>NONE</code> prevents <code>ROTATION</code> from being
+ * bound by default to the 2D valuator reads.
+ * <p>
+ * This method only configures the read listeners pre-defined by
+ * this behavior. For complete control over the read actions, access
+ * the <code>SensorEventAgent</code> used by this behavior directly.
+ *
+ * @param action either <code>ROTATION</code>, <code>TRANSLATION</code>,
+ * <code>SCALE</code>, or <code>NONE</code>
+ * @see #setRotationCoords
+ * @see #setRotationSpeed
+ * @see #setTransformCenterSource
+ * @see #setTransformCenter
+ * @see #setTranslationSpeed
+ * @see #setFastSpeedFactor
+ * @see #setScaleSpeed
+ * @see #setMatrixIndices2D
+ * @see #getSensorEventAgent
+ * @see RotationListener2D
+ * @see TranslationListener2D
+ * @see ScaleListener2D
+ */
+ public void setReadAction2D(int action) {
+ if (! (action == ROTATION || action == TRANSLATION ||
+ action == SCALE || action == NONE))
+ throw new IllegalArgumentException
+ ("\nReadAction2D must be ROTATION, TRANSLATION, SCALE, " +
+ "or NONE") ;
+
+ this.readAction2D = action ;
+ }
+
+ /**
+ * Gets the configured 2D valuator read action.
+ *
+ * @return the action associated with the sensor read
+ */
+ public int getReadAction2D() {
+ if (readAction2D == UNSET)
+ return NONE ;
+ else
+ return readAction2D ;
+ }
+
+ /**
+ * Property which sets a button action for the 2D valuator. The possible
+ * values are <code>Rotation</code>, <code>Translation</code>,
+ * <code>Scale</code>, or <code>None</code>, with a default of
+ * <code>None</code>. These actions are the same as those for
+ * <code>ReadAction2D</code>.
+ * <p>
+ * Specifying a button index that is greater that that available with the
+ * 2D valuator will result in an <code>ArrayOutOfBoundsException</code>
+ * when the behavior is initialized or attached to a
+ * <code>ViewingPlatform</code>.
+ * <p>
+ * This property is set in the configuration file read by
+ * <code>ConfiguredUniverse</code>.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * ButtonAction2D <i>&lt;button index&gt;</i>
+ * [Rotation | Translation | Scale | None])
+ *
+ * @param action array of length 2 containing a <code>Double</code> and a
+ * <code>String</code>.
+ * @see #setButtonAction2D
+ * @see #ReadAction2D ReadAction2D
+ * @see #RotationCoords RotationCoords
+ * @see #RotationSpeed RotationSpeed
+ * @see #TransformCenterSource TransformCenterSource
+ * @see #TransformCenter TransformCenter
+ * @see #TranslationSpeed TranslationSpeed
+ * @see #FastSpeedFactor FastSpeedFactor
+ * @see #ScaleSpeed ScaleSpeed
+ * @see #MatrixIndices2D MatrixIndices2D
+ * @see RotationListener2D
+ * @see TranslationListener2D
+ * @see ScaleListener2D
+ */
+ public void ButtonAction2D(Object[] action) {
+ if (! (action.length == 2 &&
+ action[0] instanceof Double && action[1] instanceof String))
+ throw new IllegalArgumentException
+ ("\nButtonAction2D must be a number and a string") ;
+
+ int button = ((Double)action[0]).intValue() ;
+ String actionString = (String)action[1] ;
+
+ if (actionString.equals("Rotation"))
+ setButtonAction2D(button, ROTATION) ;
+ else if (actionString.equals("Translation"))
+ setButtonAction2D(button, TRANSLATION) ;
+ else if (actionString.equals("Scale"))
+ setButtonAction2D(button, SCALE) ;
+ else if (actionString.equals("None"))
+ setButtonAction2D(button, NONE) ;
+ else
+ throw new IllegalArgumentException
+ ("\nButtonAction2D must be Rotation, Translation, Scale " +
+ "or None") ;
+ }
+
+ /**
+ * Sets a button action for the 2D valuator. The possible values are
+ * <code>ROTATION</code>, <code>TRANSLATION</code>, <code>SCALE</code>, or
+ * <code>NONE</code>, with a default of <code>NONE</code>. These actions
+ * are the same as those for <code>setReadAction2D</code>.
+ * <p>
+ * Specifying a button index that is greater that that available with the
+ * 2D valuator will result in an <code>ArrayOutOfBoundsException</code>
+ * when the behavior is initialized or attached to a
+ * <code>ViewingPlatform</code>.
+ * <p>
+ * This method only configures the button listeners pre-defined by
+ * this behavior. For complete control over the button actions, access
+ * the <code>SensorEventAgent</code> used by this behavior directly.
+ *
+ * @param button index of the button to bind
+ * @param action either <code>ROTATION</code>, <code>TRANSLATION</code>,
+ * <code>SCALE</code>, or <code>NONE</code>
+ * @see #setReadAction2D
+ * @see #setRotationCoords
+ * @see #setRotationSpeed
+ * @see #setTransformCenterSource
+ * @see #setTransformCenter
+ * @see #setTranslationSpeed
+ * @see #setFastSpeedFactor
+ * @see #setScaleSpeed
+ * @see #setMatrixIndices2D
+ * @see #getSensorEventAgent
+ * @see RotationListener2D
+ * @see TranslationListener2D
+ * @see ScaleListener2D
+ */
+ public synchronized void setButtonAction2D(int button, int action) {
+ if (! (action == ROTATION || action == TRANSLATION ||
+ action == SCALE || action == NONE))
+ throw new IllegalArgumentException
+ ("\naction must be ROTATION, TRANSLATION, SCALE, or NONE") ;
+
+ while (button >= buttonActions2D.size()) {
+ buttonActions2D.add(null) ;
+ }
+ buttonActions2D.set(button, new Integer(action)) ;
+ }
+
+
+ /**
+ * Gets the action associated with the specified button on the 2D valuator.
+ *
+ * @return the action associated with the button
+ */
+ public int getButtonAction2D(int button) {
+ if (button >= buttonActions2D.size())
+ return NONE ;
+
+ Integer i = (Integer)buttonActions2D.get(button) ;
+ if (i == null)
+ return NONE ;
+ else
+ return i.intValue() ;
+ }
+
+ /**
+ * Property which sets the action to be bound to 6DOF sensor reads. This
+ * action will be performed every frame whenever a button action has not
+ * been invoked.
+ * <p>
+ * The default is <code>Echo</code>, which displays a geometric
+ * representation of the sensor's current position and orientation in the
+ * virtual world. A value of <code>None</code> indicates that no default
+ * action is to be applied to the sensor's read.
+ * <p>
+ * This property is set in the configuration file read by
+ * <code>ConfiguredUniverse</code>.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * ReadAction6D [Echo | None])
+ *
+ * @param action array of length 1 containing a <code>String</code>
+ * @see #setReadAction6D
+ * @see EchoReadListener6D
+ */
+ public void ReadAction6D(Object[] action) {
+ if (! (action.length == 1 && action[0] instanceof String))
+ throw new IllegalArgumentException
+ ("\nReadAction6D must be a String") ;
+
+ String actionString = (String)action[0] ;
+
+ if (actionString.equals("Echo"))
+ setReadAction6D(ECHO) ;
+ else if (actionString.equals("None"))
+ setReadAction6D(NONE) ;
+ else
+ throw new IllegalArgumentException
+ ("\nReadAction6D must be Echo or None") ;
+ }
+
+ /**
+ * Sets the action to be bound to 6DOF sensor reads. This action will be
+ * performed every frame whenever a button action has not been invoked.
+ * <p>
+ * The default is <code>ECHO</code>, which displays a geometric
+ * representation of the sensor's current position and orientation in the
+ * virtual world. A value of <code>NONE</code> indicates that no default
+ * action is to be associated with the sensor's read.
+ * <p>
+ * This method only configures the read listeners pre-defined by
+ * this behavior. For complete control over the read actions, access
+ * the <code>SensorEventAgent</code> used by this behavior directly.
+ *
+ * @param action either <code>ECHO</code> or <code>NONE</code>
+ * @see EchoReadListener6D
+ * @see #getSensorEventAgent
+ */
+ public void setReadAction6D(int action) {
+ if (! (action == ECHO || action == NONE))
+ throw new IllegalArgumentException
+ ("\naction must be ECHO or NONE") ;
+
+ this.readAction6D = action ;
+ }
+
+ /**
+ * Gets the configured 6DOF sensor read action.
+ *
+ * @return the configured 6DOF sensor read action
+ */
+ public int getReadAction6D() {
+ if (readAction6D == UNSET)
+ return NONE ;
+ else
+ return readAction6D ;
+ }
+
+ /**
+ * Property which sets the normal translation speed. The default is 0.1
+ * meters/second in physical units. This property is set in the
+ * configuration file read by <code>ConfiguredUniverse</code>.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * TranslationSpeed <i>&lt;speed&gt;</i> [PhysicalMeters | VirtualUnits]
+ * [PerFrame | PerSecond])
+ *
+ * @param speed array of length 3; first element is a <code>Double</code>
+ * for the speed, the second is a <code>String</code> for the units, and
+ * the third is a <code>String</code> for the time base
+ * @see #setTranslationSpeed
+ */
+ public void TranslationSpeed(Object[] speed) {
+ if (! (speed.length == 3 && speed[0] instanceof Double &&
+ speed[1] instanceof String && speed[2] instanceof String))
+ throw new IllegalArgumentException
+ ("\nTranslationSpeed must be number, units, and time base") ;
+
+ double v = ((Double)speed[0]).doubleValue() ;
+ String unitsString = (String)speed[1] ;
+ String timeBaseString = (String)speed[2] ;
+ int units, timeBase ;
+
+ if (unitsString.equals("PhysicalMeters"))
+ units = PHYSICAL_METERS ;
+ else if (unitsString.equals("VirtualUnits"))
+ units = VIRTUAL_UNITS ;
+ else
+ throw new IllegalArgumentException
+ ("\nTranslationSpeed units must be " +
+ "PhysicalMeters or VirtualUnits") ;
+
+ if (timeBaseString.equals("PerFrame"))
+ timeBase = PER_FRAME ;
+ else if (timeBaseString.equals("PerSecond"))
+ timeBase = PER_SECOND ;
+ else
+ throw new IllegalArgumentException
+ ("\ntime base must be PerFrame or PerSecond") ;
+
+ setTranslationSpeed(v, units, timeBase) ;
+ }
+
+ /**
+ * Sets the normal translation speed. The default is 0.1 physical
+ * meters/second.
+ *
+ * @param speed how fast to translate
+ * @param units either <code>PHYSICAL_METERS</code> or
+ * <code>VIRTUAL_UNITS</code>
+ * @param timeBase either <code>PER_SECOND</code> or
+ * <code>PER_FRAME</code>
+ */
+ public void setTranslationSpeed(double speed, int units, int timeBase) {
+ this.translationSpeed = speed ;
+
+ if (units == PHYSICAL_METERS || units == VIRTUAL_UNITS)
+ this.translationUnits = units ;
+ else
+ throw new IllegalArgumentException
+ ("\ntranslation speed units must be " +
+ "PHYSICAL_METERS or VIRTUAL_UNITS") ;
+
+ if (timeBase == PER_FRAME || timeBase == PER_SECOND)
+ this.translationTimeBase = timeBase ;
+ else
+ throw new IllegalArgumentException
+ ("\ntranslation time base must be PER_FRAME or PER_SECOND") ;
+ }
+
+ /**
+ * Gets the normal speed at which to translate the view platform.
+ *
+ * @return the normal translation speed
+ */
+ public double getTranslationSpeed() {
+ return translationSpeed ;
+ }
+
+ /**
+ * Gets the translation speed units.
+ *
+ * @return the translation units
+ */
+ public int getTranslationUnits() {
+ return translationUnits ;
+ }
+
+ /**
+ * Gets the time base for translation speed.
+ *
+ * @return the translation time base
+ */
+ public int getTranslationTimeBase() {
+ return translationTimeBase ;
+ }
+
+ /**
+ * Property which sets the time interval for accelerating to the
+ * translation, rotation, or scale speeds and for transitioning between
+ * the normal and fast translation speeds. The default is 1 second. This
+ * property is set in the configuration file read by
+ * <code>ConfiguredUniverse</code>.<p>
+ *
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * AccelerationTime <i>&lt;seconds&gt;</i>)
+ *
+ * @param time array of length 1 containing a <code>Double</code>
+ * @see #setAccelerationTime
+ */
+ public void AccelerationTime(Object[] time) {
+ if (! (time.length == 1 && time[0] instanceof Double))
+ throw new IllegalArgumentException
+ ("\nAccelerationTime must be a number") ;
+
+ setAccelerationTime(((Double)time[0]).doubleValue()) ;
+ }
+
+ /**
+ * Sets the time interval for accelerating to the translation, rotation,
+ * or scale speeds and for transitioning between the normal and fast
+ * translation speeds. The default is 1 second.
+ *
+ * @param time number of seconds to accelerate to normal or fast speed
+ */
+ public void setAccelerationTime(double time) {
+ this.accelerationTime = time ;
+ }
+
+ /**
+ * Gets the time interval for accelerating to normal speed and for
+ * transitioning between the normal and fast translation speeds.
+ *
+ * @return the acceleration time
+ */
+ public double getAccelerationTime() {
+ return accelerationTime ;
+ }
+
+ /**
+ * Property which sets the time interval for which the translation occurs
+ * at the normal speed. The default is 8 seconds. This property is set
+ * in the configuration file read by <code>ConfiguredUniverse</code>.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * ConstantSpeedTime <i>&lt;seconds&gt;</i>)
+ *
+ * @param time array of length 1 containing a <code>Double</code>
+ * @see #setConstantSpeedTime
+ */
+ public void ConstantSpeedTime(Object[] time) {
+ if (! (time.length == 1 && time[0] instanceof Double))
+ throw new IllegalArgumentException
+ ("\nConstantSpeedTime must be a number") ;
+
+ setConstantSpeedTime(((Double)time[0]).doubleValue()) ;
+ }
+
+ /**
+ * Sets the time interval for which the translation occurs at the normal
+ * speed. The default is 8 seconds.
+ *
+ * @param time number of seconds to translate at a constant speed
+ */
+ public void setConstantSpeedTime(double time) {
+ this.constantSpeedTime = time ;
+ }
+
+ /**
+ * Gets the time interval for which the translation occurs at the
+ * normal speed.
+ *
+ * @return the constant speed time
+ */
+ public double getConstantSpeedTime() {
+ return constantSpeedTime ;
+ }
+
+ /**
+ * Property which sets the fast translation speed factor. The default is
+ * 10 times the normal speed. This property is set in the configuration
+ * file read by </code>ConfiguredUniverse</code>.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * FastSpeedFactor <i>&lt;factor&gt;</i>)
+ *
+ * @param factor array of length 1 containing a <code>Double</code>
+ * @see #setFastSpeedFactor
+ */
+ public void FastSpeedFactor(Object[] factor) {
+ if (! (factor.length == 1 && factor[0] instanceof Double))
+ throw new IllegalArgumentException
+ ("\nFastSpeedFactor must be a number") ;
+
+ setFastSpeedFactor(((Double)factor[0]).doubleValue()) ;
+ }
+
+ /**
+ * Sets the fast translation speed factor. The default is 10 times the
+ * normal speed.
+ *
+ * @param factor scale by which the normal translation speed is multiplied
+ */
+ public void setFastSpeedFactor(double factor) {
+ this.fastSpeedFactor = factor ;
+ }
+
+ /**
+ * Gets the factor by which the normal translation speed is multiplied
+ * after the constant speed time interval.
+ *
+ * @return the fast speed factor
+ */
+ public double getFastSpeedFactor() {
+ return fastSpeedFactor ;
+ }
+
+ /**
+ * Property which sets the threshold for 2D valuator reads. The default
+ * is 0.0. It can be set higher to handle noisy valuators. This property
+ * is set in the configuration file read by
+ * <code>ConfiguredUniverse</code>.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * Threshold2D <i>&lt;threshold&gt;</i>)
+ *
+ * @param threshold array of length 1 containing a <code>Double</code>
+ * @see #setThreshold2D
+ */
+ public void Threshold2D(Object[] threshold) {
+ if (! (threshold.length == 1 && threshold[0] instanceof Double))
+ throw new IllegalArgumentException
+ ("\nThreshold2D must be a number") ;
+
+ setThreshold2D(((Double)threshold[0]).doubleValue()) ;
+ }
+
+ /**
+ * Sets the threshold for 2D valuator reads. The default is 0.0. It can
+ * be set higher to handle noisy valuators.
+ *
+ * @param threshold if the absolute values of both the X and Y valuator
+ * reads are less than this value then the values are ignored
+ */
+ public void setThreshold2D(double threshold) {
+ this.threshold2D = threshold ;
+ }
+
+ /**
+ * Gets the 2D valuator threshold.
+ *
+ * @return the threshold value
+ */
+ public double getThreshold2D() {
+ return threshold2D ;
+ }
+
+ /**
+ * Property which specifies where to find the X and Y values in the matrix
+ * read generated by a 2D valuator. The defaults are along the
+ * translation components of the matrix, at indices 3 and 7. This
+ * property is set in the configuration file read by
+ * <code>ConfiguredUniverse</code>.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * MatrixIndices2D <i>&lt;X index&gt; &lt;Y index&gt;</i>)
+ *
+ * @param indices array of length 2 containing <code>Doubles</code>
+ * @see #setMatrixIndices2D
+ */
+ public void MatrixIndices2D(Object[] indices) {
+ if (! (indices.length == 2 &&
+ indices[0] instanceof Double && indices[1] instanceof Double))
+ throw new IllegalArgumentException
+ ("\nMatrixIndices2D must be a numbers") ;
+
+ setMatrixIndices2D(((Double)indices[0]).intValue(),
+ ((Double)indices[1]).intValue()) ;
+ }
+
+ /**
+ * Specifies where to find the X and Y values in the matrix read generated
+ * by a 2D valuator. The defaults are along the translation components of
+ * the matrix, at indices 3 and 7.
+ *
+ * @param xIndex index of the X valuator value
+ * @param yIndex index of the Y valuator value
+ */
+ public void setMatrixIndices2D(int xIndex, int yIndex) {
+ this.x2D = xIndex ;
+ this.y2D = yIndex ;
+ }
+
+ /**
+ * Gets the index where the X value of a 2D valuator read matrix can be
+ * found.
+ *
+ * @return the X index in the read matrix
+ */
+ public int getMatrixXIndex2D() {
+ return x2D ;
+ }
+
+ /**
+ * Gets the index where the Y value of a 2D valuator read matrix can be
+ * found.
+ *
+ * @return the Y index in the read matrix
+ */
+ public int getMatrixYIndex2D() {
+ return y2D ;
+ }
+
+ /**
+ * Property which sets the rotation speed. The default is 180
+ * degrees/second. This property is set in the configuration file read by
+ * <code>ConfiguredUniverse</code>.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * RotationSpeed <i>&lt;speed&gt;</i> [Degrees | Radians]
+ * [PerFrame | PerSecond])
+ *
+ * @param speed array of length 3; first element is a <code>Double</code>
+ * for the speed, the second is a <code>String</code> for the units, and
+ * the third is a <code>String</code> for the time base
+ * @see #setRotationSpeed
+ */
+ public void RotationSpeed(Object[] speed) {
+ if (! (speed.length == 3 && speed[0] instanceof Double &&
+ speed[1] instanceof String && speed[2] instanceof String))
+ throw new IllegalArgumentException
+ ("\nRotationSpeed must be number, units, and time base") ;
+
+ double v = ((Double)speed[0]).doubleValue() ;
+ String unitsString = (String)speed[1] ;
+ String timeBaseString = (String)speed[2] ;
+ int units, timeBase ;
+
+ if (unitsString.equals("Degrees"))
+ units = DEGREES ;
+ else if (unitsString.equals("Radians"))
+ units = RADIANS ;
+ else
+ throw new IllegalArgumentException
+ ("\nRotationSpeed units must be Degrees or Radians") ;
+
+ if (timeBaseString.equals("PerFrame"))
+ timeBase = PER_FRAME ;
+ else if (timeBaseString.equals("PerSecond"))
+ timeBase = PER_SECOND ;
+ else
+ throw new IllegalArgumentException
+ ("\nRotationSpeed time base must be PerFrame or PerSecond") ;
+
+ setRotationSpeed(v, units, timeBase) ;
+ }
+
+ /**
+ * Sets the rotation speed. The default is 180 degrees/second.
+ *
+ * @param speed how fast to rotate
+ * @param units either <code>DEGREES</code> or <code>RADIANS</code>
+ * @param timeBase either <code>PER_SECOND</code> or <code>PER_FRAME</code>
+ */
+ public void setRotationSpeed(double speed, int units, int timeBase) {
+ this.rotationSpeed = speed ;
+
+ if (units == DEGREES || units == RADIANS)
+ this.rotationUnits = units ;
+ else
+ throw new IllegalArgumentException
+ ("\nrotation speed units must be DEGREES or RADIANS") ;
+
+ if (timeBase == PER_FRAME || timeBase == PER_SECOND)
+ this.rotationTimeBase = timeBase ;
+ else
+ throw new IllegalArgumentException
+ ("\nrotation time base must be PER_FRAME or PER_SECOND") ;
+ }
+
+ /**
+ * Gets the rotation speed.
+ *
+ * @return the rotation speed
+ */
+ public double getRotationSpeed() {
+ return rotationSpeed ;
+ }
+
+ /**
+ * Gets the rotation speed units
+ *
+ * @return the rotation units
+ */
+ public int getRotationUnits() {
+ return rotationUnits ;
+ }
+
+ /**
+ * Gets the time base for rotation speed.
+ *
+ * @return the rotation time base
+ */
+ public int getRotationTimeBase() {
+ return rotationTimeBase ;
+ }
+
+ /**
+ * Property which sets the rotation coordinate system. The default is
+ * <code>Sensor</code>, which means that the rotation axis is parallel to
+ * the XY plane of the current orientation of a specified 6DOF sensor. A
+ * value of <code>ViewPlatform</code> means the rotation axis is parallel
+ * to the XY plane of the view platform. The latter is also the fallback
+ * if a 6DOF sensor is not specified. If the value is <code>Head</code>,
+ * then the rotation occurs in head coordinates.
+ * <p>
+ * This property is set in the configuration file read by
+ * <code>ConfiguredUniverse</code>.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * RotationCoords [Sensor | ViewPlatform | Head])
+ *
+ * @param coords array of length 1 containing a <code>String</code>
+ * @see #setRotationCoords
+ */
+ public void RotationCoords(Object[] coords) {
+ if (! (coords.length == 1 && coords[0] instanceof String))
+ throw new IllegalArgumentException
+ ("\nRotationCoords must be a String") ;
+
+ String coordsString = (String)coords[0] ;
+
+ if (coordsString.equals("Sensor"))
+ setRotationCoords(SENSOR) ;
+ else if (coordsString.equals("ViewPlatform"))
+ setRotationCoords(VIEW_PLATFORM) ;
+ else if (coordsString.equals("Head"))
+ setRotationCoords(HEAD) ;
+ else
+ throw new IllegalArgumentException
+ ("\nRotationCoords must be Sensor, ViewPlatform, or Head") ;
+ }
+
+ /**
+ * Sets the rotation coordinate system. The default is
+ * <code>SENSOR</code>, which means that the rotation axis is parallel to
+ * the XY plane of the current orientation of a specified 6DOF sensor. A
+ * value of <code>VIEW_PLATFORM</code> means the rotation axis is parallel
+ * to the XY plane of the view platform. The latter is also the fallback
+ * if a 6DOF sensor is not specified. If the value is <code>HEAD</code>,
+ * then rotation occurs in head coordinates.
+ *
+ * @param coords either <code>SENSOR</code>, <code>VIEW_PLATFORM</code>, or
+ * <code>HEAD</code>
+ */
+ public void setRotationCoords(int coords) {
+ if (! (coords == SENSOR || coords == VIEW_PLATFORM || coords == HEAD))
+ throw new IllegalArgumentException
+ ("\nrotation coordinates be SENSOR, VIEW_PLATFORM, or HEAD") ;
+
+ this.rotationCoords = coords ;
+ }
+
+ /**
+ * Gets the rotation coordinate system.
+ *
+ * @return the rotation coordinate system
+ */
+ public int getRotationCoords() {
+ return rotationCoords ;
+ }
+
+ /**
+ * Property which sets the scaling speed. The default is 2.0 per second,
+ * which means magnification doubles the apparent size of the virtual
+ * world every second, and minification halves the apparent size of the
+ * virtual world every second.
+ * <p>
+ * The scaling applied with each frame is <code>Math.pow(scaleSpeed,
+ * frameTime)</code>, where <code>frameTime</code> is the time in seconds
+ * that the last frame took to render if the time base is
+ * <code>PerSecond</code>, or 1.0 if the time base is
+ * <code>PerFrame</code>. If scaling is performed with the 2D valuator,
+ * then the valuator's Y value as specified by
+ * <code>MatrixIndices2D</code> is an additional scale applied to the
+ * exponent. If scaling is performed by the 6DOF sensor, then the scale
+ * speed can be inverted with a negative exponent by using the appropriate
+ * listener constructor flag.
+ * <p>
+ * This property is set in the configuration file read by
+ * <code>ConfiguredUniverse</code>.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * ScaleSpeed <i>&lt;speed&gt;</i> [PerFrame | PerSecond])
+ *
+ * @param speed array of length 2; first element is a <code>Double</code>
+ * for the speed, and the second is a <code>String</code> for the time
+ * base
+ * @see #setScaleSpeed
+ */
+ public void ScaleSpeed(Object[] speed) {
+ if (! (speed.length == 2 &&
+ speed[0] instanceof Double && speed[1] instanceof String))
+ throw new IllegalArgumentException
+ ("\nScalingSpeed must be a number and a string") ;
+
+ double v = ((Double)speed[0]).doubleValue() ;
+ String timeBaseString = (String)speed[2] ;
+ int timeBase ;
+
+ if (timeBaseString.equals("PerFrame"))
+ timeBase = PER_FRAME ;
+ else if (timeBaseString.equals("PerSecond"))
+ timeBase = PER_SECOND ;
+ else
+ throw new IllegalArgumentException
+ ("\nScalingSpeed time base must be PerFrame or PerSecond") ;
+
+ setScaleSpeed(v, timeBase) ;
+ }
+
+ /**
+ * Sets the scaling speed. The default is 2.0 per second, which means
+ * magnification doubles the apparent size of the virtual world every
+ * second, and minification halves the apparent size of the virtual world
+ * every second.
+ * <p>
+ * The scaling applied with each frame is <code>Math.pow(scaleSpeed,
+ * frameTime)</code>, where <code>frameTime</code> is the time in seconds
+ * that the last frame took to render if the time base is
+ * <code>PER_SECOND</code>, or 1.0 if the time base is
+ * <code>PER_FRAME</code>. If scaling is performed with the 2D valuator,
+ * then the valuator's Y value as specified by
+ * <code>setMatrixIndices2D</code> is an additional scale applied to the
+ * exponent. If scaling is performed by the 6DOF sensor, then the scale
+ * speed can be inverted with a negative exponent by using the appropriate
+ * listener constructor flag.
+ *
+ * @param speed specifies the scale speed
+ * @param timeBase either <code>PER_SECOND</code> or <code>PER_FRAME</code>
+ */
+ public void setScaleSpeed(double speed, int timeBase) {
+ this.scaleSpeed = speed ;
+
+ if (timeBase == PER_FRAME || timeBase == PER_SECOND)
+ this.scaleTimeBase = timeBase ;
+ else
+ throw new IllegalArgumentException
+ ("\nscaling time base must be PER_FRAME or PER_SECOND") ;
+ }
+
+ /**
+ * Gets the scaling speed.
+ *
+ * @return the scaling speed
+ */
+ public double getScaleSpeed() {
+ return scaleSpeed ;
+ }
+
+ /**
+ * Gets the time base for scaling speed.
+ *
+ * @return the scaling time base
+ */
+ public int getScaleTimeBase() {
+ return scaleTimeBase ;
+ }
+
+ /**
+ * Property which sets the source of the center of rotation and scale.
+ * The default is <code>Hotspot</code>, which means the center of rotation
+ * or scale is a 6DOF sensor's current hotspot location. The alternative
+ * is <code>VworldFixed</code>, which uses the fixed virtual world
+ * coordinates specified by the <code>TransformCenter</code> property as
+ * the center. The latter is also the fallback if a 6DOF sensor is not
+ * specified. This property is set in the configuration file read by
+ * <code>ConfiguredUniverse</code>.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * TransformCenterSource [Hotspot | VworldFixed])
+ *
+ * @param source array of length 1 containing a <code>String</code>
+ * @see #setTransformCenterSource
+ */
+ public void TransformCenterSource(Object[] source) {
+ if (! (source.length == 1 && source[0] instanceof String))
+ throw new IllegalArgumentException
+ ("\nTransformCenterSource must be a String") ;
+
+ String sourceString = (String)source[0] ;
+
+ if (sourceString.equals("Hotspot"))
+ setTransformCenterSource(HOTSPOT) ;
+ else if (sourceString.equals("VworldFixed"))
+ setTransformCenterSource(VWORLD_FIXED) ;
+ else
+ throw new IllegalArgumentException
+ ("\nTransformCenterSource must be Hotspot or " +
+ "VworldFixed") ;
+ }
+
+ /**
+ * Sets the source of the center of rotation and scale. The default is
+ * <code>HOTSPOT</code>, which means the center of rotation or scale is a
+ * 6DOF sensor's current hotspot location. The alternative is
+ * <code>VWORLD_FIXED</code>, which uses the fixed virtual world
+ * coordinates specified by <code>setTransformCenter</code> as the center.
+ * The latter is also the fallback if a 6DOF sensor is not specified.
+ * <p>
+ * The transform center source can be dynamically updated while the
+ * behavior is running.
+ *
+ * @param source either <code>HOTSPOT</code> or <code>VWORLD_FIXED</code>
+ */
+ public void setTransformCenterSource(int source) {
+ if (! (source == HOTSPOT || source == VWORLD_FIXED))
+ throw new IllegalArgumentException
+ ("\nrotation/scale center source must be HOTSPOT or " +
+ "VWORLD_FIXED") ;
+
+ this.transformCenterSource = source ;
+ }
+
+ /**
+ * Gets the rotation/scale center source.
+ *
+ * @return the rotation/scale center source
+ */
+ public int getTransformCenterSource() {
+ return transformCenterSource ;
+ }
+
+ /**
+ * Property which sets the center of rotation and scale if the
+ * <code>TransformCenterSource</code> property is <code>VworldFixed</code>
+ * or if a 6DOF sensor is not specified. The default is (0.0, 0.0, 0.0)
+ * in virtual world coordinates. This property is set in the
+ * configuration file read by <code>ConfiguredUniverse</code>.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * TransformCenter <i>&lt;Point3d&gt;</i>)
+ *
+ * @param center array of length 1 containing a <code>Point3d</code>
+ * @see #setTransformCenter
+ */
+ public void TransformCenter(Object[] center) {
+ if (! (center.length == 1 && center[0] instanceof Point3d))
+ throw new IllegalArgumentException
+ ("\nTransformCenter must be a Point3d") ;
+
+ setTransformCenter((Point3d)center[0]) ;
+ }
+
+ /**
+ * Sets the center of rotation and scale if
+ * <code>setTransformCenterSource</code> is called with
+ * <code>VWORLD_FIXED</code> or if a 6DOF sensor is not specified. The
+ * default is (0.0, 0.0, 0.0) in virtual world coordinates.
+ * <p>
+ * The transform center can be dynamically updated while the behavior is
+ * running.
+ *
+ * @param center point in virtual world coordinates about which to rotate
+ * and scale
+ */
+ public void setTransformCenter(Point3d center) {
+ this.transformCenter.set(center) ;
+ }
+
+ /**
+ * Gets the rotation/scale center in virtual world coordinates.
+ * @param center <code>Point3d</code> to receive a copy of the
+ * rotation/scale center
+ */
+ public void getTransformCenter(Point3d center) {
+ center.set(transformCenter) ;
+ }
+
+ /**
+ * Property which sets the nominal sensor rotation. The default is the
+ * identity transform.
+ * <p>
+ * This behavior assumes that when a hand-held wand is pointed directly at
+ * a screen in an upright position, then its 6DOF sensor's local
+ * coordinate system axes (its basis vectors) are nominally aligned with
+ * the image plate basis vectors; specifically, that the sensor's -Z axis
+ * points toward the screen, the +Y axis points up, and the +X axis points
+ * to the right. The translation and rotation listeners provided by this
+ * behavior assume this orientation to determine the transforms to be
+ * applied to the view platform; for example, translation applies along
+ * the sensor Z axis, while rotation applies about axes defined in the
+ * sensor XY plane.
+ * <p>
+ * This nominal alignment may not hold true depending upon how the sensor
+ * is mounted and how the specific <code>InputDevice</code> supporting the
+ * sensor handles its orientation. The <code>NominalSensorRotation</code>
+ * property can be used to correct the alignment by specifying the
+ * rotation needed to transform vectors from the nominal sensor coordinate
+ * system, aligned with the image plate coordinate system as described
+ * above, to the sensor's actual local coordinate system.
+ * <p>
+ * NOTE: the nominal sensor transform applies <i>only</i> to the
+ * translation directions and rotation axes created by the listeners
+ * defined in this behavior; for compatibility with the core Java 3D API,
+ * sensor reads and the sensor hotspot location are still expressed in the
+ * sensor's local coordinate system.
+ * <p>
+ * This property is set in the configuration file read by
+ * <code>ConfiguredUniverse</code>.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * NominalSensorRotation [<i>&lt;Matrix4d&gt;</i> |
+ * <i>&lt;Matrix3d&gt;</i>])
+ *
+ * @param matrix array of length 1 containing a <code>Matrix4d</code> or
+ * <code>Matrix3d</code>
+ * @see #setNominalSensorRotation
+ */
+ public void NominalSensorRotation(Object[] matrix) {
+ if (! (matrix.length == 1 && (matrix[0] instanceof Matrix3d ||
+ matrix[0] instanceof Matrix4d)))
+ throw new IllegalArgumentException
+ ("\nNominalSensorRotation must be a Matrix3d or Matrix4d") ;
+
+ Transform3D t3d = new Transform3D() ;
+
+ if (matrix[0] instanceof Matrix3d)
+ t3d.set((Matrix3d)matrix[0]) ;
+ else
+ t3d.set((Matrix4d)matrix[0]) ;
+
+ setNominalSensorRotation(t3d) ;
+ }
+
+ /**
+ * Sets the nominal sensor transform. The default is the identity
+ * transform.
+ * <p>
+ * This behavior assumes that when a hand-held wand is pointed directly at
+ * a screen in an upright position, then its 6DOF sensor's local
+ * coordinate system axes (its basis vectors) are nominally aligned with
+ * the image plate basis vectors; specifically, that the sensor's -Z axis
+ * points toward the screen, the +Y axis points up, and the +X axis points
+ * to the right. The translation and rotation listeners provided by this
+ * behavior assume this orientation to determine the transforms to be
+ * applied to the view platform, in that translation applies along the
+ * sensor Z axis, and rotation applies about axes defined in the sensor XY
+ * plane.
+ * <p>
+ * This nominal alignment may not hold true depending upon how the sensor
+ * is mounted and how the specific <code>InputDevice</code> supporting the
+ * sensor handles its orientation. <code>setNominalSensorRotation</code>
+ * can be called to correct the alignment by specifying the rotation
+ * needed to transform vectors from the nominal sensor coordinate system,
+ * aligned with the image plate coordinate system as described above, to
+ * the sensor's actual local coordinate system.
+ * <p>
+ * NOTE: the nominal sensor transform applies <i>only</i> to the
+ * translation directions and rotation axes created by the listeners
+ * defined in this behavior; for compatibility with the core Java 3D API,
+ * sensor reads and the sensor hotspot location are still expressed in the
+ * sensor's local coordinate system.
+ *
+ * @param transform Rotates vectors from the nominal sensor coordinate
+ * system system to the sensor's local coordinate system; only the
+ * rotational components are used. May be set <code>null</code> for
+ * identity.
+ */
+ public void setNominalSensorRotation(Transform3D transform) {
+ if (transform == null) {
+ nominalSensorRotation = null ;
+ return ;
+ }
+
+ if (nominalSensorRotation == null)
+ nominalSensorRotation = new Transform3D() ;
+
+ // Set transform and make sure it is a rotation only.
+ nominalSensorRotation.set(transform) ;
+ nominalSensorRotation.setTranslation(new Vector3d()) ;
+ }
+
+ /**
+ * Gets the nominal sensor transform.
+ *
+ * @param t3d <code>Transform3D</code> to receive a copy of the
+ * nominal sensor transform
+ */
+ public void getNominalSensorRotation(Transform3D t3d) {
+ t3d.set(nominalSensorRotation) ;
+ }
+
+ /**
+ * Property which sets the number of buttons to be pressed simultaneously
+ * on the 6DOF sensor in order to reset the view back to the home
+ * transform. The value must be greater than 1; the default is 3. A
+ * value of <code>None</code> disables this action. This property is set
+ * in the configuration file read by <code>ConfiguredUniverse</code>.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * ResetViewButtonCount6D [<i>&lt;count&gt;</i> | None])
+ *
+ * @param count array of length 1 containing a <code>Double</code> or
+ * <code>String</code>
+ * @see #setResetViewButtonCount6D
+ * @see ViewPlatformBehavior#setHomeTransform
+ * ViewPlatformBehavior.setHomeTransform
+ */
+ public void ResetViewButtonCount6D(Object[] count) {
+ if (! (count.length == 1 &&
+ (count[0] instanceof Double || count[0] instanceof String)))
+ throw new IllegalArgumentException
+ ("\nResetViewButtonCount6D must be a number or None") ;
+
+ if (count[0] instanceof String) {
+ String s = (String)count[0] ;
+ if (s.equals("None"))
+ setResetViewButtonCount6D(NONE) ;
+ else
+ throw new IllegalArgumentException
+ ("\nResetViewButtonCount6D string value must be None") ;
+ }
+ else {
+ setResetViewButtonCount6D(((Double)count[0]).intValue()) ;
+ }
+ }
+
+ /**
+ * Sets the number of buttons to be pressed simultaneously
+ * on the 6DOF sensor in order to reset the view back to the home
+ * transform. The value must be greater than 1; the default is 3. A
+ * value of <code>NONE</code> disables this action.
+ *
+ * @param count either <code>NONE</code> or button count > 1
+ * @see ViewPlatformBehavior#setHomeTransform
+ * ViewPlatformBehavior.setHomeTransform
+ */
+ public void setResetViewButtonCount6D(int count) {
+ if (count == NONE || count > 1) {
+ resetViewButtonCount6D = count ;
+ }
+ else {
+ throw new IllegalArgumentException
+ ("reset view button count must be > 1") ;
+ }
+ }
+
+ /**
+ * Gets the number of buttons to be pressed simultaneously on the 6DOF
+ * sensor in order to reset the view back to the home transform. A value
+ * of <code>NONE</code> indicates this action is disabled.
+ *
+ * @return the number of buttons to press simultaneously for a view reset
+ */
+ public int getResetViewButtonCount6D() {
+ return resetViewButtonCount6D ;
+ }
+
+ /**
+ * Property which sets the number of buttons to be pressed simultaneously
+ * on the 2D valuator in order to reset the view back to the home
+ * transform. The value must be greater than 1; the default is
+ * <code>None</code>. A value of <code>None</code> disables this action.
+ * This property is set in the configuration file read by
+ * <code>ConfiguredUniverse</code>.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * ResetViewButtonCount2D [<i>&lt;count&gt;</i> | None])
+ *
+ * @param count array of length 1 containing a <code>Double</code> or
+ * <code>String</code>
+ * @see #setResetViewButtonCount2D
+ * @see ViewPlatformBehavior#setHomeTransform
+ * ViewPlatformBehavior.setHomeTransform
+ */
+ public void ResetViewButtonCount2D(Object[] count) {
+ if (! (count.length == 1 &&
+ (count[0] instanceof Double || count[0] instanceof String)))
+ throw new IllegalArgumentException
+ ("\nResetViewButtonCount2D must be a number or None") ;
+
+ if (count[0] instanceof String) {
+ String s = (String)count[0] ;
+ if (s.equals("None"))
+ setResetViewButtonCount2D(NONE) ;
+ else
+ throw new IllegalArgumentException
+ ("\nResetViewButtonCount2D string value must be None") ;
+ }
+ else {
+ setResetViewButtonCount2D(((Double)count[0]).intValue()) ;
+ }
+ }
+
+ /**
+ * Sets the number of buttons to be pressed simultaneously on the 2D
+ * valuator in order to reset the view back to the home transform. The
+ * value must be greater than 1; the default is <code>NONE</code>. A
+ * value of <code>NONE</code> disables this action.
+ *
+ * @param count either <code>NONE</code> or button count > 1
+ * @see ViewPlatformBehavior#setHomeTransform
+ * ViewPlatformBehavior.setHomeTransform
+ */
+ public void setResetViewButtonCount2D(int count) {
+ if (count == NONE || count > 1) {
+ resetViewButtonCount2D = count ;
+ }
+ else {
+ throw new IllegalArgumentException
+ ("reset view button count must be > 1") ;
+ }
+ }
+
+ /**
+ * Gets the number of buttons to be pressed simultaneously on the 2D
+ * valuator in order to reset the view back to the home transform. A value
+ * of <code>NONE</code> indicates this action is disabled.
+ *
+ * @return the number of buttons to press simultaneously for a view reset
+ */
+ public int getResetViewButtonCount2D() {
+ return resetViewButtonCount2D ;
+ }
+
+ /**
+ * Property which sets the 6DOF sensor echo type. The default is
+ * <code>Gnomon</code>, which displays an object with points indicating
+ * the direction of each of the sensor's coordinate system axes at the
+ * location of the sensor's hotspot. The alternative is
+ * <code>Beam</code>, which displays a beam from the sensor's origin to
+ * the location of the sensor's hotspot; the hotspot must not be (0, 0, 0)
+ * or an <code>IllegalArgumentException</code> will result. The width of
+ * each of these echo types is specified by the <code>EchoSize</code>
+ * property. The <code>EchoType</code> property is set in the
+ * configuration file read by <code>ConfiguredUniverse</code>.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * EchoType [Gnomon | Beam | None])
+ *
+ * @param type array of length 1 containing a <code>String</code>
+ * @see #setEchoType
+ */
+ public void EchoType(Object[] type) {
+ if (! (type.length == 1 && type[0] instanceof String))
+ throw new IllegalArgumentException
+ ("\nEchoType must be a String") ;
+
+ String typeString = (String)type[0] ;
+
+ if (typeString.equals("Gnomon"))
+ setEchoType(GNOMON) ;
+ else if (typeString.equals("Beam"))
+ setEchoType(BEAM) ;
+ else if (typeString.equals("None"))
+ setEchoType(NONE) ;
+ else
+ throw new IllegalArgumentException
+ ("\nEchoType must be Gnomon, Beam, or None") ;
+ }
+
+ /**
+ * Sets the 6DOF sensor echo type. The default is <code>GNOMON</code>,
+ * which displays an object with points indicating the direction of each
+ * of the sensor's coordinate axes at the location of the sensor's
+ * hotspot. The alternative is <code>BEAM</code>, which displays a beam
+ * from the sensor's origin to the location of the sensor's hotspot; the
+ * hotspot must not be (0, 0, 0) or an
+ * <code>IllegalArgumentException</code> will result. The width of each
+ * of these echo types is specified by <code>setEchoSize</code>.
+ *
+ * @param type <code>GNOMON</code>, <code>BEAM</code>, or
+ * <code>NONE</code> are recognized
+ */
+ public void setEchoType(int type) {
+ this.echoType = type ;
+ }
+
+ /**
+ * Gets the echo type.
+ *
+ * @return the echo type
+ */
+ public int getEchoType() {
+ return echoType ;
+ }
+
+ /**
+ * Property which sets the size of the 6DOF sensor echo in physical
+ * meters. This is used for the width of the gnomon and beam echoes. The
+ * default is 1 centimeter. This property is set in the configuration
+ * file read by <code>ConfiguredUniverse</code>.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * EchoSize <i>&lt;size&gt;</i>)
+ *
+ * @param echoSize array of length 1 containing a <code>Double</code>
+ * @see #setEchoSize
+ */
+ public void EchoSize(Object[] echoSize) {
+ if (! (echoSize.length == 1 && echoSize[0] instanceof Double))
+ throw new IllegalArgumentException
+ ("\nEchoSize must be a Double") ;
+
+ setEchoSize(((Double)echoSize[0]).doubleValue()) ;
+ }
+
+ /**
+ * Sets the size of the 6DOF sensor echo in physical meters. This is used
+ * for the width of the gnomon and beam echoes. The default is 1
+ * centimeter.
+ *
+ * @param echoSize the size in meters
+ */
+ public void setEchoSize(double echoSize) {
+ this.echoSize = echoSize ;
+ }
+
+ /**
+ * Gets the size of the 6DOF sensor echo in meters.
+ *
+ * @return the echo size
+ */
+ public double getEchoSize() {
+ return echoSize ;
+ }
+
+ /**
+ * Property which sets the color of the 6DOF sensor echo. The default is
+ * white. This property is set in the configuration file read by
+ * <code>ConfiguredUniverse</code>.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * EchoColor <i>&lt;red&gt; &lt;green&gt; &lt;blue&gt;</i>)
+ *
+ * @param color array of length 3 containing <code>Doubles</code>
+ * @see #setEchoColor
+ */
+ public void EchoColor(Object[] color) {
+ if (! (color.length == 3 && color[0] instanceof Double &&
+ color[1] instanceof Double && color[2] instanceof Double))
+ throw new IllegalArgumentException
+ ("\nEchoColor must be 3 numbers for red, green, and blue") ;
+
+ setEchoColor(new Color3f(((Double)color[0]).floatValue(),
+ ((Double)color[1]).floatValue(),
+ ((Double)color[2]).floatValue())) ;
+ }
+
+ /**
+ * Sets the color of the 6DOF sensor echo. The default is white. This
+ * can be called to set the color before or after the echo geometry is
+ * created.
+ *
+ * @param color the echo color
+ */
+ public void setEchoColor(Color3f color) {
+ if (echoColor == null)
+ echoColor = new Color3f(color) ;
+ else
+ echoColor.set(color) ;
+
+ if (echoGeometry != null) {
+ Appearance a = echoGeometry.getAppearance() ;
+ Material m = a.getMaterial() ;
+ m.setDiffuseColor(echoColor) ;
+ }
+ }
+
+ /**
+ * Gets the 6DOF sensor echo color.
+ *
+ * @param color the <code>Color3f</code> into which to copy the echo color
+ */
+ public void getEchoColor(Color3f color) {
+ if (echoColor == null)
+ color.set(1.0f, 1.0f, 1.0f) ;
+ else
+ color.set(echoColor) ;
+ }
+
+ /**
+ * Property which sets the 6DOF sensor echo transparency. The default is
+ * opaque. A value of 0.0 is fully opaque and 1.0 is fully transparent.
+ * This property is set in the configuration file read by
+ * <code>ConfiguredUniverse</code>.
+ * <p>
+ * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
+ * EchoTransparency <i>&lt;transparency&gt;</i>)
+ *
+ * @param transparency array of length 1 containing a <code>Double</code>
+ * @see #setEchoTransparency
+ */
+ public void EchoTransparency(Object[] transparency) {
+ if (! (transparency.length == 1 && transparency[0] instanceof Double))
+ throw new IllegalArgumentException
+ ("\nEchoTransparency must be a number") ;
+
+ setEchoTransparency(((Double)transparency[0]).floatValue()) ;
+ }
+
+ /**
+ * Sets the 6DOF sensor echo transparency. The default is opaque. A
+ * value of 0.0 is fully opaque and 1.0 is fully transparent. This can be
+ * called to set the transparency before or after the echo geometry is
+ * created.
+ *
+ * @param transparency the transparency value
+ */
+ public void setEchoTransparency(float transparency) {
+ echoTransparency = transparency ;
+
+ if (echoGeometry != null) {
+ Appearance a = echoGeometry.getAppearance() ;
+ TransparencyAttributes ta = a.getTransparencyAttributes() ;
+ if (echoTransparency == 0.0f) {
+ ta.setTransparencyMode(TransparencyAttributes.NONE) ;
+ ta.setTransparency(0.0f) ;
+ }
+ else {
+ ta.setTransparencyMode(TransparencyAttributes.BLENDED) ;
+ ta.setTransparency(echoTransparency) ;
+ // Use order independent additive blend for gnomon.
+ if (echoGeometry instanceof SensorGnomonEcho)
+ ta.setDstBlendFunction(TransparencyAttributes.BLEND_ONE) ;
+ }
+ }
+ }
+
+ /**
+ * Gets the 6DOF sensor echo transparency value.
+ *
+ * @return the transparency value
+ */
+ public float getEchoTransparency() {
+ return echoTransparency ;
+ }
+
+ /**
+ * Sets the transform group containing a 6DOF sensor's echo geometry.
+ * This is used to specify a custom echo. Its transform will be
+ * manipulated to represent the position and orientation of the 6DOF
+ * sensor. Capabilities to allow writing its transform and to read,
+ * write, and extend its children will be set.
+ * <p>
+ * This method must be called before the behavior is made live in order to
+ * have an effect.
+ *
+ * @param echo the <code>TransformGroup</code> containing the
+ * echo geometry
+ */
+ public void setEchoTransformGroup(TransformGroup echo) {
+ echo.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE) ;
+ echo.setCapability(Group.ALLOW_CHILDREN_READ) ;
+ echo.setCapability(Group.ALLOW_CHILDREN_WRITE) ;
+ echo.setCapability(Group.ALLOW_CHILDREN_EXTEND) ;
+ this.echoTransformGroup = echo ;
+ }
+
+ /**
+ * Gets the transform group containing a 6DOF sensor's echo geometry.
+ * Capabilities to write its transform and read, write, and extend its
+ * children are granted.
+ *
+ * @return the echo's transform group
+ */
+ public TransformGroup getEchoTransformGroup() {
+ return echoTransformGroup ;
+ }
+
+ /**
+ * Gets the <code>Shape3D</code> defining the 6DOF sensor's echo geometry
+ * and appearance. The returned <code>Shape3D</code> allows appearance
+ * read and write. If a custom echo was supplied by providing the echo
+ * transform group directly then the return value will be
+ * <code>null</code>.
+ *
+ * @return the echo geometry, or <code>null</code> if a custom echo was
+ * supplied
+ */
+ public Shape3D getEchoGeometry() {
+ return echoGeometry ;
+ }
+
+ /**
+ * Gets the <code>SensorEventAgent</code> used by this behavior. Sensor
+ * event generation is delegated to this agent. This can be accessed to
+ * manipulate the sensor button and read action bindings directly.
+ *
+ * @return the sensor event agent
+ */
+ public SensorEventAgent getSensorEventAgent() {
+ return eventAgent ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/compression/CommandStream.java b/src/classes/share/com/sun/j3d/utils/compression/CommandStream.java
new file mode 100644
index 0000000..b8aff95
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/compression/CommandStream.java
@@ -0,0 +1,265 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.compression ;
+
+/**
+ * This class is used to build the bit-level compression command stream which
+ * is the final result of the compression process. It defines the bit
+ * representations of the compression commands and provides a mechanism for
+ * the interleaving and forwarding of command headers and bodies required by
+ * the geometry compression specification.
+ */
+class CommandStream {
+ // Geometry compression commands.
+ static final int SET_NORM = 0xC0 ;
+ static final int SET_COLOR = 0x80 ;
+ static final int VERTEX = 0x40 ;
+ static final int MESH_B_R = 0x20 ;
+ static final int SET_STATE = 0x18 ;
+ static final int SET_TABLE = 0x10 ;
+ static final int V_NO_OP = 0x01 ;
+
+ // Huffman table indices.
+ static final int POSITION_TABLE = 0 ;
+ static final int COLOR_TABLE = 1 ;
+ static final int NORMAL_TABLE = 2 ;
+
+ // The buffer of compressed data and the current offset.
+ private byte bytes[] ;
+ private int byteOffset ;
+ private int bitOffset ;
+
+ // Last command body for header forwarding.
+ private long lastBody ;
+ private int lastBodyLength ;
+
+ /**
+ * Create an empty CommandStream with a default initial size.
+ */
+ CommandStream() {
+ this(65536) ;
+ }
+
+ /**
+ * Create an empty CommandStream with the given initial size.
+ *
+ * @param initSize initial capacity of CommandStream in bytes
+ */
+ CommandStream(int initSize) {
+ bytes = new byte[initSize] ;
+ clear() ;
+ }
+
+ /**
+ * Mark the CommandStream as empty so that its storage will be reused.
+ */
+ void clear() {
+ // Initialize the first byte to 0.
+ // Subsequent bytes are cleared as they are written.
+ bytes[0] = 0 ;
+
+ // Reset the number of valid bits.
+ bitOffset = 0 ;
+ byteOffset = 0 ;
+
+ // The first command header is always followed by the body of an
+ // implicit variable length no-op to start the header-forwarding
+ // interleave required by hardware decompressor implementations. The
+ // only necessary bits are 5 bits of length set to zeros to indicate a
+ // fill of zero length.
+ lastBody = 0 ;
+ lastBodyLength = 5 ;
+ }
+
+ /**
+ * Add a compression command to this instance.<p>
+ *
+ * A compression command includes an 8-bit header and can range up to 72
+ * bits in length. The command with the maximum length is a 2-bit color
+ * command with a 6-bit tag in the header, followed by four 16-bit color
+ * components of data.<p>
+ *
+ * A subcommand is either a position, normal, or color, though in practice
+ * a position subcommand can only be part of a vertex command. Normal and
+ * color subcommands can be parts of separate global normal and color
+ * commands as well as parts of a vertex command.<p>
+ *
+ * A subcommand includes a 6-bit header. Its length is 2 bits less than
+ * the length of the corresponding command.
+ *
+ * @param header contains compression command header bits, right-justified
+ * within the bits of the int
+ * @param headerLength number of bits in header, either 8 for commands or
+ * 6 for subcommands
+ * @param body contains the body of the compression command,
+ * right-justified within the bits of the long
+ * @param bodyLength number of bits in the body
+ */
+ void addCommand(int header, int headerLength, long body, int bodyLength) {
+ addByte(header, headerLength) ;
+ addLong(lastBody, lastBodyLength) ;
+
+ lastBody = body ;
+ lastBodyLength = bodyLength ;
+ }
+
+ //
+ // Add the rightmost bitCount bits of b to the end of the command stream.
+ //
+ private void addByte(int b, int bitCount) {
+ int bitsEmpty = 8 - bitOffset ;
+ b &= (int)CompressionStreamElement.lengthMask[bitCount] ;
+
+ if (bitCount <= bitsEmpty) {
+ bytes[byteOffset] |= (b << (bitsEmpty - bitCount)) ;
+ bitOffset += bitCount ;
+ return ;
+ }
+
+ if (bytes.length == byteOffset + 1) {
+ byte newBytes[] = new byte[bytes.length * 2] ;
+ System.arraycopy(bytes, 0, newBytes, 0, bytes.length) ;
+ bytes = newBytes ;
+ }
+
+ bitOffset = bitCount - bitsEmpty ;
+ bytes[byteOffset] |= (b >>> bitOffset) ;
+
+ byteOffset++ ;
+ bytes[byteOffset] = (byte)(b << (8 - bitOffset)) ;
+ }
+
+ //
+ // Add the rightmost bitCount bits of l to the end of the command stream.
+ //
+ private void addLong(long l, int bitCount) {
+ int byteCount = bitCount / 8 ;
+ int excessBits = bitCount - byteCount * 8 ;
+
+ if (excessBits > 0)
+ addByte((int)(l >>> (byteCount * 8)), excessBits) ;
+
+ while (byteCount > 0) {
+ addByte((int)((l >>> ((byteCount - 1) * 8)) & 0xff), 8) ;
+ byteCount-- ;
+ }
+ }
+
+ /**
+ * Add a no-op and the last command body. Pad out with additional no-ops
+ * to a 64-bit boundary if necessary. A call to this method is required
+ * in order to create a valid compression command stream.
+ */
+ void end() {
+ int excessBytes, padBits ;
+
+ // Add the 1st no-op and the last body.
+ addByte(V_NO_OP, 8) ;
+ addLong(lastBody, lastBodyLength) ;
+
+ excessBytes = (byteOffset + 1) % 8 ;
+ if (excessBytes == 0 && bitOffset == 8)
+ // No padding necessary.
+ return ;
+
+ // Need to add padding with a 2nd no-op.
+ addByte(V_NO_OP, 8) ;
+ excessBytes = (byteOffset + 1) % 8 ;
+
+ if (excessBytes == 0)
+ padBits = 8 - bitOffset ;
+ else {
+ int fillBytes = 8 - excessBytes ;
+ padBits = (8 * fillBytes) + (8 - bitOffset) ;
+ }
+
+ // The minimum length for a no-op command body is 5 bits.
+ if (padBits < 5)
+ // Have to cross the next 64-bit boundary.
+ padBits += 64 ;
+
+ // The maximum length of a no-op body is a 5-bit length + 31 bits of
+ // fill for a total of 36.
+ if (padBits < 37) {
+ // Pad with the body of the 1st no-op.
+ addLong((padBits - 5) << (padBits - 5), padBits) ;
+ return ;
+ }
+
+ // The number of bits to pad at this point is [37..68]. Knock off 24
+ // bits with the body of the 1st no-op to reduce the number of pad
+ // bits to [13..44], which can be filled with 1 more no-op.
+ addLong(19 << 19, 24) ;
+ padBits -= 24 ;
+
+ // Add a 3rd no-op.
+ addByte(V_NO_OP, 8) ;
+ padBits -= 8 ;
+
+ // Complete padding with the body of the 2nd no-op.
+ addLong((padBits - 5) << (padBits - 5), padBits) ;
+ }
+
+ /**
+ * Get the number of bytes in the compression command stream.
+ *
+ * @return size of compressed data in bytes
+ */
+ int getByteCount() {
+ if (byteOffset + bitOffset == 0)
+ return 0 ;
+ else
+ return byteOffset + 1 ;
+ }
+
+ /**
+ * Get the bytes composing the compression command stream.
+ *
+ * @return reference to array of bytes containing the compressed data
+ */
+ byte[] getBytes() {
+ return bytes ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/compression/CompressedGeometryFile.java b/src/classes/share/com/sun/j3d/utils/compression/CompressedGeometryFile.java
new file mode 100644
index 0000000..9cb6048
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/compression/CompressedGeometryFile.java
@@ -0,0 +1,1007 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.compression ;
+
+import java.io.* ;
+import javax.media.j3d.* ;
+
+//
+// The compressed geometry file format supported by this class has a 32
+// byte header followed by multiple compressed geometry objects.
+//
+// Each object consists of a block of compressed data and an 8-byte
+// individual block header describing its contents.
+//
+// The file ends with a directory data structure used for random access,
+// containing a 64-bit offset for each object in the order in which it
+// appears in the file. This is also used to find the size of the largest
+// object in the file and must be present.
+//
+
+/**
+ * This class provides methods to read and write compressed geometry resource
+ * files. These files usually end with the .cg extension and support
+ * sequential as well as random access to multiple compressed geometry
+ * objects.
+ */
+public class CompressedGeometryFile {
+ private static final boolean print = false ;
+ private static final boolean benchmark = false ;
+
+ /**
+ * The magic number which identifies the compressed geometry file type.
+ */
+ static final int MAGIC_NUMBER = 0xbaddfab4 ;
+
+ /**
+ * Byte offset of the magic number from start of file.
+ */
+ static final int MAGIC_NUMBER_OFFSET = 0 ;
+
+ /**
+ * Byte offset of the major version number from start of file.
+ */
+ static final int MAJOR_VERSION_OFFSET = 4 ;
+
+ /**
+ * Byte offset of the minor version number from start of file.
+ */
+ static final int MINOR_VERSION_OFFSET = 8 ;
+
+ /**
+ * Byte offset of the minor minor version number from start of file.
+ */
+ static final int MINOR_MINOR_VERSION_OFFSET = 12 ;
+
+ /**
+ * Byte offset of the number of objects from start of file.
+ */
+ static final int OBJECT_COUNT_OFFSET = 16 ;
+
+ /**
+ * Byte offset of the directory offset from start of file.
+ * This offset is long word aligned since the directory offset is a long.
+ */
+ static final int DIRECTORY_OFFSET_OFFSET = 24 ;
+
+ /**
+ * File header total size in bytes.
+ */
+ static final int HEADER_SIZE = 32 ;
+
+ /**
+ * Byte offset of the object size from start of individual compressed
+ * geometry block.
+ */
+ static final int OBJECT_SIZE_OFFSET = 0 ;
+
+ /**
+ * Byte offset of the compressed geometry data descriptor from start of
+ * individual compressed geometry block.
+ */
+ static final int GEOM_DATA_OFFSET = 4 ;
+
+ /**
+ * Bits in compressed geometry data descriptor which encode the buffer type.
+ */
+ static final int TYPE_MASK = 0x03 ;
+
+ /**
+ * Bit in compressed geometry data descriptor encoding presence of normals.
+ */
+ static final int NORMAL_PRESENT_MASK = 0x04 ;
+
+ /**
+ * Bit in compressed geometry data descriptor encoding presence of colors.
+ */
+ static final int COLOR_PRESENT_MASK = 0x08 ;
+
+ /**
+ * Bit in compressed geometry data descriptor encoding presence of alphas.
+ */
+ static final int ALPHA_PRESENT_MASK = 0x10 ;
+
+ /**
+ * Value in compressed geometry data descriptor for a point buffer type.
+ */
+ static final int TYPE_POINT = 1 ;
+
+ /**
+ * Value in compressed geometry data descriptor for a line buffer type.
+ */
+ static final int TYPE_LINE = 2 ;
+
+ /**
+ * Value in compressed geometry data descriptor for a triangle buffer type.
+ */
+ static final int TYPE_TRIANGLE = 3 ;
+
+ /**
+ * Block header total size in bytes.
+ */
+ static final int BLOCK_HEADER_SIZE = 8 ;
+
+ // The name of the compressed geometry resource file.
+ String fileName = null ;
+
+ // The major, minor, and subminor version number of the most recent
+ // compressor used to compress any of the objects in the compressed
+ // geometry resource file.
+ int majorVersionNumber ;
+ int minorVersionNumber ;
+ int minorMinorVersionNumber ;
+
+ // The number of objects in the compressed geometry resource file.
+ int objectCount ;
+
+ // The index of the current object in the file.
+ int objectIndex = 0 ;
+
+ // The random access file associated with this instance.
+ RandomAccessFile cgFile = null ;
+
+ // The magic number identifying the file type.
+ int magicNumber ;
+
+ // These fields are set from each individual block of compressed geometry.
+ byte cgBuffer[] ;
+ int geomSize ;
+ int geomStart ;
+ int geomDataType ;
+
+ // The directory of object offsets is read from the end of the file.
+ long directory[] ;
+ long directoryOffset ;
+
+ // The object sizes are computed from the directory offsets. These are
+ // used to allocate a buffer large enough to hold the largest object and
+ // to determine how many consecutive objects can be read into that buffer.
+ int objectSizes[] ;
+ int bufferObjectStart ;
+ int bufferObjectCount ;
+ int bufferNextObjectCount ;
+ int bufferNextObjectOffset ;
+
+ // The shared compressed geometry header object.
+ CompressedGeometryHeader cgh ;
+
+ // Flag indicating file update.
+ boolean fileUpdate = false ;
+
+ /**
+ * Construct a new CompressedGeometryFile instance associated with the
+ * specified file. An attempt is made to open the file with read-only
+ * access; if this fails then a FileNotFoundException is thrown.
+ *
+ * @param file path to the compressed geometry resource file
+ * @exception FileNotFoundException if file doesn't exist or
+ * cannot be read
+ * @exception IllegalArgumentException if the file is not a compressed
+ * geometry resource file
+ * @exception IOException if there is a header or directory read error
+ */
+ public CompressedGeometryFile(String file) throws IOException {
+ this(file, false) ;
+ }
+
+ /**
+ * Construct a new CompressedGeometryFile instance associated with the
+ * specified file.
+ *
+ * @param file path to the compressed geometry resource file
+ * @param rw if true, opens the file for read and write access or attempts
+ * to create one if it doesn't exist; if false, opens the file with
+ * read-only access
+ * @exception FileNotFoundException if file doesn't exist or
+ * access permissions disallow access
+ * @exception IllegalArgumentException if the file is not a compressed
+ * geometry resource file
+ * @exception IOException if there is a header or directory read error
+ */
+ public CompressedGeometryFile(String file, boolean rw) throws IOException {
+ // Open the file and read the file header.
+ open(file, rw) ;
+
+ // Copy the file name.
+ fileName = new String(file) ;
+
+ // Set up the file fields.
+ initialize() ;
+ }
+
+ /**
+ * Construct a new CompressedGeometryFile instance associated with a
+ * currently open RandomAccessFile.
+ *
+ * @param file currently open RandomAccessFile
+ * @exception IllegalArgumentException if the file is not a compressed
+ * geometry resource file
+ * @exception IOException if there is a header or directory read error
+ */
+ public CompressedGeometryFile(RandomAccessFile file) throws IOException {
+ // Copy the file reference.
+ cgFile = file ;
+
+ // Set up the file fields.
+ initialize() ;
+ }
+
+ /**
+ * Delete all compressed objects from this instance. This method may only
+ * be called after successfully creating a CompressedGeometryFile instance
+ * with read-write access, so a corrupted or otherwise invalid resource
+ * must be removed manually before it can be rewritten. The close()
+ * method must be called sometime after invoking clear() in order to write
+ * out the new directory structure.
+ *
+ * @exception IOException if clear fails
+ */
+ public void clear() throws IOException {
+ // Truncate the file.
+ cgFile.setLength(0) ;
+
+ // Set up the file fields.
+ initialize() ;
+ }
+
+ /**
+ * Return a string containing the file name associated with this instance
+ * or null if there is none.
+ *
+ * @return file name associated with this instance or null if there is
+ * none
+ */
+ public String getFileName() {
+ return fileName ;
+ }
+
+ /**
+ * Return the major version number of the most recent compressor used to
+ * compress any of the objects in this instance.
+ *
+ * @return major version number
+ */
+ public int getMajorVersionNumber() {
+ return majorVersionNumber ;
+ }
+
+ /**
+ * Return the minor version number of the most recent compressor used to
+ * compress any of the objects in this instance.
+ *
+ * @return minor version number
+ */
+ public int getMinorVersionNumber() {
+ return minorVersionNumber ;
+ }
+
+ /**
+ * Return the subminor version number of the most recent compressor used to
+ * compress any of the objects in this instance.
+ *
+ * @return subminor version number
+ */
+ public int getMinorMinorVersionNumber() {
+ return minorMinorVersionNumber ;
+ }
+
+ /**
+ * Return the number of compressed objects in this instance.
+ *
+ * @return number of compressed objects
+ */
+ public int getObjectCount() {
+ return objectCount ;
+ }
+
+ /**
+ * Return the current object index associated with this instance. This is
+ * the index of the object that would be returned by an immediately
+ * following call to the readNext() method. Its initial value is 0; -1
+ * is returned if the last object has been read.
+ *
+ * @return current object index, or -1 if at end
+ */
+ public int getCurrentIndex() {
+ if (objectIndex == objectCount)
+ return -1 ;
+ else
+ return objectIndex ;
+ }
+
+ /**
+ * Read the next compressed geometry object in the instance. This is
+ * initially the first object (index 0) in the instance; otherwise, it is
+ * whatever object is next after the last one read. The current object
+ * index is incremented by 1 after the read. When the last object is read
+ * the index becomes invalid and an immediately subsequent call to
+ * readNext() returns null.
+ *
+ * @return a CompressedGeometry node component, or null if the last object
+ * has been read
+ * @exception IOException if read fails
+ */
+ public CompressedGeometry readNext() throws IOException {
+ return readNext(cgBuffer.length) ;
+ }
+
+ /**
+ * Read all compressed geometry objects contained in the instance. The
+ * current object index becomes invalid; an immediately following call
+ * to readNext() will return null.
+ *
+ * @return an array of CompressedGeometry node components.
+ * @exception IOException if read fails
+ */
+ public CompressedGeometry[] read() throws IOException {
+ long startTime = 0 ;
+ CompressedGeometry cg[] = new CompressedGeometry[objectCount] ;
+
+ if (benchmark)
+ startTime = System.currentTimeMillis() ;
+
+ objectIndex = 0 ;
+ setFilePointer(directory[0]) ;
+ bufferNextObjectCount = 0 ;
+
+ for (int i = 0 ; i < objectCount ; i++)
+ cg[i] = readNext(cgBuffer.length) ;
+
+ if (benchmark) {
+ long t = System.currentTimeMillis() - startTime ;
+ System.out.println("read " + objectCount +
+ " objects " + cgFile.length() +
+ " bytes in " + (t/1000f) + " sec.") ;
+ System.out.println((cgFile.length()/(float)t) + " Kbytes/sec.") ;
+ }
+
+ return cg ;
+ }
+
+ /**
+ * Read the compressed geometry object at the specified index. The
+ * current object index is set to the subsequent object unless the last
+ * object has been read, in which case the index becomes invalid and an
+ * immediately following call to readNext() will return null.
+ *
+ * @param index compressed geometry object to read
+ * @return a CompressedGeometry node component
+ * @exception IndexOutOfBoundsException if object index is
+ * out of range
+ * @exception IOException if read fails
+ */
+ public CompressedGeometry read(int index) throws IOException {
+ objectIndex = index ;
+
+ if (objectIndex < 0) {
+ throw new IndexOutOfBoundsException
+ ("\nobject index must be >= 0") ;
+ }
+ if (objectIndex >= objectCount) {
+ throw new IndexOutOfBoundsException
+ ("\nobject index must be < " + objectCount) ;
+ }
+
+ // Check if object is in cache.
+ if ((objectIndex >= bufferObjectStart) &&
+ (objectIndex < bufferObjectStart + bufferObjectCount)) {
+ if (print) System.out.println("\ngetting object from cache\n") ;
+
+ bufferNextObjectOffset = (int)
+ (directory[objectIndex] - directory[bufferObjectStart]) ;
+
+ bufferNextObjectCount =
+ bufferObjectCount - (objectIndex - bufferObjectStart) ;
+
+ return readNext() ;
+
+ } else {
+ // Move file pointer to correct offset.
+ setFilePointer(directory[objectIndex]) ;
+
+ // Force a read from current offset. Disable cache read-ahead
+ // since cache hits are unlikely with random access.
+ bufferNextObjectCount = 0 ;
+ return readNext(objectSizes[objectIndex]) ;
+ }
+ }
+
+
+ /**
+ * Add a compressed geometry node component to the end of the instance.
+ * The current object index becomes invalid; an immediately following call
+ * to readNext() will return null. The close() method must be called at
+ * some later time in order to create a valid compressed geometry file.
+ *
+ * @param cg a compressed geometry node component
+ * @exception CapabilityNotSetException if unable to get compressed
+ * geometry data from the node component
+ * @exception IOException if write fails
+ */
+ public void write(CompressedGeometry cg) throws IOException {
+ CompressedGeometryHeader cgh = new CompressedGeometryHeader() ;
+ cg.getCompressedGeometryHeader(cgh) ;
+
+ // Update the read/write buffer size if necessary.
+ if (cgh.size + BLOCK_HEADER_SIZE > cgBuffer.length) {
+ cgBuffer = new byte[cgh.size + BLOCK_HEADER_SIZE] ;
+ if (print) System.out.println("\ncgBuffer: reallocated " +
+ (cgh.size+BLOCK_HEADER_SIZE) +
+ " bytes") ;
+ }
+
+ cg.getCompressedGeometry(cgBuffer) ;
+ write(cgh, cgBuffer) ;
+ }
+
+ /**
+ * Add a buffer of compressed geometry data to the end of the
+ * resource. The current object index becomes invalid; an immediately
+ * following call to readNext() will return null. The close() method must
+ * be called at some later time in order to create a valid compressed
+ * geometry file.
+ *
+ * @param cgh a CompressedGeometryHeader object describing the data.
+ * @param geometry the compressed geometry data
+ * @exception IOException if write fails
+ */
+ public void write(CompressedGeometryHeader cgh, byte geometry[])
+ throws IOException {
+
+ // Update the read/write buffer size if necessary. It won't be used
+ // in this method, but should be big enough to read any object in
+ // the file, including the one to be written.
+ if (cgh.size + BLOCK_HEADER_SIZE > cgBuffer.length) {
+ cgBuffer = new byte[cgh.size + BLOCK_HEADER_SIZE] ;
+ if (print) System.out.println("\ncgBuffer: reallocated " +
+ (cgh.size+BLOCK_HEADER_SIZE) +
+ " bytes") ;
+ }
+
+ // Assuming backward compatibility, the version number of the file
+ // should be the maximum of all individual compressed object versions.
+ if ((cgh.majorVersionNumber > majorVersionNumber)
+ ||
+ ((cgh.majorVersionNumber == majorVersionNumber) &&
+ (cgh.minorVersionNumber > minorVersionNumber))
+ ||
+ ((cgh.majorVersionNumber == majorVersionNumber) &&
+ (cgh.minorVersionNumber == minorVersionNumber) &&
+ (cgh.minorMinorVersionNumber > minorMinorVersionNumber))) {
+
+ majorVersionNumber = cgh.majorVersionNumber ;
+ minorVersionNumber = cgh.minorVersionNumber ;
+ minorMinorVersionNumber = cgh.minorMinorVersionNumber ;
+
+ this.cgh.majorVersionNumber = cgh.majorVersionNumber ;
+ this.cgh.minorVersionNumber = cgh.minorVersionNumber ;
+ this.cgh.minorMinorVersionNumber = cgh.minorMinorVersionNumber ;
+ }
+
+ // Get the buffer type and see what vertex components are present.
+ int geomDataType = 0 ;
+
+ switch (cgh.bufferType) {
+ case CompressedGeometryHeader.POINT_BUFFER:
+ geomDataType = TYPE_POINT ;
+ break ;
+ case CompressedGeometryHeader.LINE_BUFFER:
+ geomDataType = TYPE_LINE ;
+ break ;
+ case CompressedGeometryHeader.TRIANGLE_BUFFER:
+ geomDataType = TYPE_TRIANGLE ;
+ break ;
+ }
+
+ if ((cgh.bufferDataPresent &
+ CompressedGeometryHeader.NORMAL_IN_BUFFER) != 0)
+ geomDataType |= NORMAL_PRESENT_MASK ;
+
+ if ((cgh.bufferDataPresent &
+ CompressedGeometryHeader.COLOR_IN_BUFFER) != 0)
+ geomDataType |= COLOR_PRESENT_MASK ;
+
+ if ((cgh.bufferDataPresent &
+ CompressedGeometryHeader.ALPHA_IN_BUFFER) != 0)
+ geomDataType |= ALPHA_PRESENT_MASK ;
+
+ // Allocate new directory and object size arrays if necessary.
+ if (objectCount == directory.length) {
+ long newDirectory[] = new long[2*objectCount] ;
+ int newObjectSizes[] = new int[2*objectCount] ;
+
+ System.arraycopy(directory, 0,
+ newDirectory, 0, objectCount) ;
+ System.arraycopy(objectSizes, 0,
+ newObjectSizes, 0, objectCount) ;
+
+ directory = newDirectory ;
+ objectSizes = newObjectSizes ;
+
+ if (print)
+ System.out.println("\ndirectory and size arrays: reallocated " +
+ (2*objectCount) + " entries") ;
+ }
+
+ // Update directory and object size array.
+ directory[objectCount] = directoryOffset ;
+ objectSizes[objectCount] = cgh.size + BLOCK_HEADER_SIZE ;
+ objectCount++ ;
+
+ // Seek to the directory and overwrite from there.
+ setFilePointer(directoryOffset) ;
+ cgFile.writeInt(cgh.size) ;
+ cgFile.writeInt(geomDataType) ;
+ cgFile.write(geometry, 0, cgh.size) ;
+ if (print)
+ System.out.println("\nwrote " + cgh.size +
+ " byte compressed object to " + fileName +
+ "\nfile offset " + directoryOffset) ;
+
+ // Update the directory offset.
+ directoryOffset += cgh.size + BLOCK_HEADER_SIZE ;
+
+ // Return end-of-file on next read.
+ objectIndex = objectCount ;
+
+ // Flag file update so close() will write out the directory.
+ fileUpdate = true ;
+ }
+
+ /**
+ * Release the resources associated with this instance.
+ * Write out final header and directory if contents were updated.
+ * This method must be called in order to create a valid compressed
+ * geometry resource file if any updates were made.
+ */
+ public void close() {
+ if (cgFile != null) {
+ try {
+ if (fileUpdate) {
+ writeFileDirectory() ;
+ writeFileHeader() ;
+ }
+ cgFile.close() ;
+ }
+ catch (IOException e) {
+ // Don't propagate this exception.
+ System.out.println("\nException: " + e.getMessage()) ;
+ System.out.println("failed to close " + fileName) ;
+ }
+ }
+ cgFile = null ;
+ cgBuffer = null ;
+ directory = null ;
+ objectSizes = null ;
+ }
+
+
+ //
+ // Open the file. Specifying a non-existent file creates a new one if
+ // access permissions allow.
+ //
+ void open(String fname, boolean rw)
+ throws FileNotFoundException, IOException {
+
+ cgFile = null ;
+ String mode ;
+
+ if (rw)
+ mode = "rw" ;
+ else
+ mode = "r" ;
+
+ try {
+ cgFile = new RandomAccessFile(fname, mode) ;
+ if (print) System.out.println("\n" + fname +
+ ": opened mode " + mode) ;
+ }
+ catch (FileNotFoundException e) {
+ // N.B. this exception is also thrown on access permission errors
+ throw new FileNotFoundException(e.getMessage() + "\n" + fname +
+ ": open mode " + mode + " failed") ;
+ }
+ }
+
+ //
+ // Seek to the specified offset in the file.
+ //
+ void setFilePointer(long offset) throws IOException {
+ cgFile.seek(offset) ;
+
+ // Reset number of objects that can be read sequentially from cache.
+ bufferNextObjectCount = 0 ;
+ }
+
+ //
+ // Initialize directory, object size array, read/write buffer, and the
+ // shared compressed geometry header.
+ //
+ void initialize() throws IOException {
+ int maxSize = 0 ;
+
+ if (cgFile.length() == 0) {
+ // New file for writing: allocate nominal initial sizes for arrays.
+ objectCount = 0 ;
+ cgBuffer = new byte[32768] ;
+ directory = new long[16] ;
+ objectSizes = new int[directory.length] ;
+
+ // Set fields as if they have been read.
+ magicNumber = MAGIC_NUMBER ;
+ majorVersionNumber = 1 ;
+ minorVersionNumber = 0 ;
+ minorMinorVersionNumber = 0 ;
+ directoryOffset = HEADER_SIZE ;
+
+ // Write the file header.
+ writeFileHeader() ;
+
+ } else {
+ // Read the file header.
+ readFileHeader() ;
+
+ // Check file type.
+ if (magicNumber != MAGIC_NUMBER) {
+ close() ;
+ throw new IllegalArgumentException
+ ("\n" + fileName + " is not a compressed geometry file") ;
+ }
+
+ // Read the directory and determine object sizes.
+ directory = new long[objectCount] ;
+ readDirectory(directoryOffset, directory) ;
+
+ objectSizes = new int[objectCount] ;
+ for (int i = 0 ; i < objectCount-1 ; i++) {
+ objectSizes[i] = (int)(directory[i+1] - directory[i]) ;
+ if (objectSizes[i] > maxSize) maxSize = objectSizes[i] ;
+ }
+
+ if (objectCount > 0) {
+ objectSizes[objectCount-1] =
+ (int)(directoryOffset - directory[objectCount-1]) ;
+
+ if (objectSizes[objectCount-1] > maxSize)
+ maxSize = objectSizes[objectCount-1] ;
+ }
+
+ // Allocate a buffer big enough to read the largest object.
+ cgBuffer = new byte[maxSize] ;
+
+ // Move to the first object.
+ setFilePointer(HEADER_SIZE) ;
+ }
+
+ // Set up common parts of the compressed geometry object header.
+ cgh = new CompressedGeometryHeader() ;
+ cgh.majorVersionNumber = this.majorVersionNumber ;
+ cgh.minorVersionNumber = this.minorVersionNumber ;
+ cgh.minorMinorVersionNumber = this.minorMinorVersionNumber ;
+
+ if (print) {
+ System.out.println(fileName + ": " + objectCount + " objects") ;
+ System.out.println("magic number 0x" +
+ Integer.toHexString(magicNumber) +
+ ", version number " + majorVersionNumber +
+ "." + minorVersionNumber +
+ "." + minorMinorVersionNumber) ;
+ System.out.println("largest object is " + maxSize + " bytes") ;
+ }
+ }
+
+ //
+ // Read the file header.
+ //
+ void readFileHeader() throws IOException {
+ byte header[] = new byte[HEADER_SIZE] ;
+
+ try {
+ setFilePointer(0) ;
+ if (cgFile.read(header) != HEADER_SIZE) {
+ close() ;
+ throw new IOException("failed header read") ;
+ }
+ }
+ catch (IOException e) {
+ if (cgFile != null) {
+ close() ;
+ }
+ throw e ;
+ }
+
+ magicNumber =
+ ((header[MAGIC_NUMBER_OFFSET+0] & 0xff) << 24) |
+ ((header[MAGIC_NUMBER_OFFSET+1] & 0xff) << 16) |
+ ((header[MAGIC_NUMBER_OFFSET+2] & 0xff) << 8) |
+ ((header[MAGIC_NUMBER_OFFSET+3] & 0xff)) ;
+
+ majorVersionNumber =
+ ((header[MAJOR_VERSION_OFFSET+0] & 0xff) << 24) |
+ ((header[MAJOR_VERSION_OFFSET+1] & 0xff) << 16) |
+ ((header[MAJOR_VERSION_OFFSET+2] & 0xff) << 8) |
+ ((header[MAJOR_VERSION_OFFSET+3] & 0xff)) ;
+
+ minorVersionNumber =
+ ((header[MINOR_VERSION_OFFSET+0] & 0xff) << 24) |
+ ((header[MINOR_VERSION_OFFSET+1] & 0xff) << 16) |
+ ((header[MINOR_VERSION_OFFSET+2] & 0xff) << 8) |
+ ((header[MINOR_VERSION_OFFSET+3] & 0xff)) ;
+
+ minorMinorVersionNumber =
+ ((header[MINOR_MINOR_VERSION_OFFSET+0] & 0xff) << 24) |
+ ((header[MINOR_MINOR_VERSION_OFFSET+1] & 0xff) << 16) |
+ ((header[MINOR_MINOR_VERSION_OFFSET+2] & 0xff) << 8) |
+ ((header[MINOR_MINOR_VERSION_OFFSET+3] & 0xff)) ;
+
+ objectCount =
+ ((header[OBJECT_COUNT_OFFSET+0] & 0xff) << 24) |
+ ((header[OBJECT_COUNT_OFFSET+1] & 0xff) << 16) |
+ ((header[OBJECT_COUNT_OFFSET+2] & 0xff) << 8) |
+ ((header[OBJECT_COUNT_OFFSET+3] & 0xff)) ;
+
+ directoryOffset =
+ ((long)(header[DIRECTORY_OFFSET_OFFSET+0] & 0xff) << 56) |
+ ((long)(header[DIRECTORY_OFFSET_OFFSET+1] & 0xff) << 48) |
+ ((long)(header[DIRECTORY_OFFSET_OFFSET+2] & 0xff) << 40) |
+ ((long)(header[DIRECTORY_OFFSET_OFFSET+3] & 0xff) << 32) |
+ ((long)(header[DIRECTORY_OFFSET_OFFSET+4] & 0xff) << 24) |
+ ((long)(header[DIRECTORY_OFFSET_OFFSET+5] & 0xff) << 16) |
+ ((long)(header[DIRECTORY_OFFSET_OFFSET+6] & 0xff) << 8) |
+ ((long)(header[DIRECTORY_OFFSET_OFFSET+7] & 0xff)) ;
+ }
+
+ //
+ // Write the file header based on current field values.
+ //
+ void writeFileHeader() throws IOException {
+ setFilePointer(0) ;
+ try {
+ cgFile.writeInt(MAGIC_NUMBER) ;
+ cgFile.writeInt(majorVersionNumber) ;
+ cgFile.writeInt(minorVersionNumber) ;
+ cgFile.writeInt(minorMinorVersionNumber) ;
+ cgFile.writeInt(objectCount) ;
+ cgFile.writeInt(0) ; // long word alignment
+ cgFile.writeLong(directoryOffset) ;
+ if (print)
+ System.out.println("wrote file header for " + fileName) ;
+ }
+ catch (IOException e) {
+ throw new IOException
+ (e.getMessage() +
+ "\ncould not write file header for " + fileName) ;
+ }
+ }
+
+ //
+ // Read the directory of compressed geometry object offsets.
+ //
+ void readDirectory(long offset, long[] directory)
+ throws IOException {
+
+ byte buff[] = new byte[directory.length * 8] ;
+ setFilePointer(offset) ;
+
+ try {
+ cgFile.read(buff) ;
+ if (print)
+ System.out.println("read " + buff.length + " byte directory") ;
+ }
+ catch (IOException e) {
+ throw new IOException
+ (e.getMessage() +
+ "\nfailed to read " + buff.length +
+ " byte directory, offset " + offset + " in file " + fileName) ;
+ }
+
+ for (int i = 0 ; i < directory.length ; i++) {
+ directory[i] =
+ ((long)(buff[i*8+0] & 0xff) << 56) |
+ ((long)(buff[i*8+1] & 0xff) << 48) |
+ ((long)(buff[i*8+2] & 0xff) << 40) |
+ ((long)(buff[i*8+3] & 0xff) << 32) |
+ ((long)(buff[i*8+4] & 0xff) << 24) |
+ ((long)(buff[i*8+5] & 0xff) << 16) |
+ ((long)(buff[i*8+6] & 0xff) << 8) |
+ ((long)(buff[i*8+7] & 0xff)) ;
+ }
+ }
+
+ //
+ // Write the file directory.
+ //
+ void writeFileDirectory() throws IOException {
+ setFilePointer(directoryOffset) ;
+
+ int directoryAlign = (int)(directoryOffset % 8) ;
+ if (directoryAlign != 0) {
+ // Align to long word before writing directory of long offsets.
+ byte bytes[] = new byte[8-directoryAlign] ;
+
+ try {
+ cgFile.write(bytes) ;
+ if (print)
+ System.out.println ("wrote " + (8-directoryAlign) +
+ " bytes long alignment") ;
+ }
+ catch (IOException e) {
+ throw new IOException
+ (e.getMessage() +
+ "\ncould not write " + directoryAlign +
+ " bytes to long word align directory for " + fileName) ;
+ }
+ directoryOffset += 8-directoryAlign ;
+ }
+
+ try {
+ for (int i = 0 ; i < objectCount ; i++)
+ cgFile.writeLong(directory[i]) ;
+
+ if (print)
+ System.out.println("wrote file directory for " + fileName) ;
+ }
+ catch (IOException e) {
+ throw new IOException
+ (e.getMessage() +
+ "\ncould not write directory for " + fileName) ;
+ }
+ }
+
+ //
+ // Get the next compressed object in the file, either from the read-ahead
+ // cache or from the file itself.
+ //
+ CompressedGeometry readNext(int bufferReadLimit)
+ throws IOException {
+ if (objectIndex == objectCount)
+ return null ;
+
+ if (bufferNextObjectCount == 0) {
+ // No valid objects are in the cache.
+ int curSize = 0 ;
+ bufferObjectCount = 0 ;
+
+ // See how much we have room to read.
+ for (int i = objectIndex ; i < objectCount ; i++) {
+ if (curSize + objectSizes[i] > bufferReadLimit) break ;
+ curSize += objectSizes[i] ;
+ bufferObjectCount++ ;
+ }
+
+ // Try to read that amount.
+ try {
+ int n = cgFile.read(cgBuffer, 0, curSize) ;
+ if (print)
+ System.out.println("\nread " + n +
+ " bytes from " + fileName) ;
+ }
+ catch (IOException e) {
+ throw new IOException
+ (e.getMessage() +
+ "\nfailed to read " + curSize +
+ " bytes, object " + objectIndex + " in file " + fileName) ;
+ }
+
+ // Point at the first object in the buffer.
+ bufferObjectStart = objectIndex ;
+ bufferNextObjectCount = bufferObjectCount ;
+ bufferNextObjectOffset = 0 ;
+ }
+
+ // Get block header info.
+ geomSize =
+ ((cgBuffer[bufferNextObjectOffset+OBJECT_SIZE_OFFSET+0]&0xff)<<24) |
+ ((cgBuffer[bufferNextObjectOffset+OBJECT_SIZE_OFFSET+1]&0xff)<<16) |
+ ((cgBuffer[bufferNextObjectOffset+OBJECT_SIZE_OFFSET+2]&0xff)<< 8) |
+ ((cgBuffer[bufferNextObjectOffset+OBJECT_SIZE_OFFSET+3]&0xff)) ;
+
+ geomDataType =
+ ((cgBuffer[bufferNextObjectOffset+GEOM_DATA_OFFSET+0]&0xff) << 24) |
+ ((cgBuffer[bufferNextObjectOffset+GEOM_DATA_OFFSET+1]&0xff) << 16) |
+ ((cgBuffer[bufferNextObjectOffset+GEOM_DATA_OFFSET+2]&0xff) << 8) |
+ ((cgBuffer[bufferNextObjectOffset+GEOM_DATA_OFFSET+3]&0xff)) ;
+
+ // Get offset of compressed geometry data from start of buffer.
+ geomStart = bufferNextObjectOffset + BLOCK_HEADER_SIZE ;
+
+ if (print) {
+ System.out.println("\nobject " + objectIndex +
+ "\nfile offset " + directory[objectIndex] +
+ ", buffer offset " + bufferNextObjectOffset) ;
+ System.out.println("size " + geomSize + " bytes, " +
+ "data descriptor 0x" +
+ Integer.toHexString(geomDataType)) ;
+ }
+
+ // Update cache info.
+ bufferNextObjectOffset += objectSizes[objectIndex] ;
+ bufferNextObjectCount-- ;
+ objectIndex++ ;
+
+ return newCG(geomSize, geomStart, geomDataType) ;
+ }
+
+
+ //
+ // Construct and return a compressed geometry node.
+ //
+ CompressedGeometry newCG(int geomSize,
+ int geomStart,
+ int geomDataType) {
+ cgh.size = geomSize ;
+ cgh.start = geomStart ;
+
+ if ((geomDataType & TYPE_MASK) == TYPE_POINT)
+ cgh.bufferType = CompressedGeometryHeader.POINT_BUFFER ;
+ else if ((geomDataType & TYPE_MASK) == TYPE_LINE)
+ cgh.bufferType = CompressedGeometryHeader.LINE_BUFFER ;
+ else if ((geomDataType & TYPE_MASK) == TYPE_TRIANGLE)
+ cgh.bufferType = CompressedGeometryHeader.TRIANGLE_BUFFER ;
+
+ cgh.bufferDataPresent = 0 ;
+
+ if ((geomDataType & NORMAL_PRESENT_MASK) != 0)
+ cgh.bufferDataPresent |=
+ CompressedGeometryHeader.NORMAL_IN_BUFFER ;
+
+ if ((geomDataType & COLOR_PRESENT_MASK) != 0)
+ cgh.bufferDataPresent |=
+ CompressedGeometryHeader.COLOR_IN_BUFFER ;
+
+ if ((geomDataType & ALPHA_PRESENT_MASK) != 0)
+ cgh.bufferDataPresent |=
+ CompressedGeometryHeader.ALPHA_IN_BUFFER ;
+
+ return new CompressedGeometry(cgh, cgBuffer) ;
+ }
+
+ /**
+ * Release file resources when this object is garbage collected.
+ */
+ protected void finalize() {
+ close() ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/compression/CompressionStream.java b/src/classes/share/com/sun/j3d/utils/compression/CompressionStream.java
new file mode 100644
index 0000000..add5799
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/compression/CompressionStream.java
@@ -0,0 +1,2294 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.compression ;
+
+import java.util.* ;
+import javax.vecmath.* ;
+import javax.media.j3d.* ;
+import com.sun.j3d.utils.geometry.* ;
+import com.sun.j3d.internal.ByteBufferWrapper ;
+import com.sun.j3d.internal.BufferWrapper ;
+import com.sun.j3d.internal.FloatBufferWrapper ;
+import com.sun.j3d.internal.DoubleBufferWrapper ;
+
+/**
+ * This class is used as input to a geometry compressor. It collects elements
+ * such as vertices, normals, colors, mesh references, and quantization
+ * parameters in an ordered stream. This stream is then traversed during
+ * the compression process and used to build the compressed output buffer.
+ *
+ * @see GeometryCompressor
+ */
+public class CompressionStream {
+ //
+ // NOTE: For now, copies are made of all GeometryArray vertex components
+ // even when by-reference access is available.
+ //
+ // TODO: Retrofit all CompressionStreamElements and MeshBuffer to handle
+ // offsets to vertex data array references so that vertex components don't
+ // have to be copied. New CompressionStreamElements could be defined to
+ // set the current array reference during the quantization pass, or the
+ // reference could be included in every CompressionStreamElement along
+ // with the data offsets.
+ //
+ // TODO: Quantize on-the-fly when adding GeometryArray vertex data so that
+ // CompressionStreamElements don't need references to the original float,
+ // double, or byte data. Quantization is currently a separate pass since
+ // the 1st pass adds vertex data and gets the total object bounds, but
+ // this can be computed by merging the bounds of each GeometryArray
+ // compressed into a single object. The 2nd pass quantization is still
+ // needed for vertex data which isn't retrieved from a GeometryArray; for
+ // example, apps that might use the addVertex() methods directly instead
+ // of addGeometryArray().
+ //
+ // TODO: To further optimize memory, create new subclasses of
+ // CompressionStream{Color, Normal} for bundled attributes and add them as
+ // explicit stream elements. Then CompressionStreamVertex won't need to
+ // carry references to them. This memory savings might be negated by the
+ // extra overhead of adding more elements to the stream, however.
+ //
+ // TODO: Keep the absolute quantized values in the mesh buffer mirror so
+ // that unmeshed CompressionStreamElements don't need to carry them.
+ //
+ // TODO: Support texture coordinate compression even though Level II is
+ // not supported by any hardware decompressor on any graphics card.
+ // Software decompression is still useful for applications interested in
+ // minimizing file space, transmission time, and object loading time.
+ //
+ private static final boolean debug = false ;
+ private static final boolean benchmark = false ;
+
+ // Mesh buffer normal substitution is unavailable in Level I.
+ private static final boolean noMeshNormalSubstitution = true ;
+
+ /**
+ * This flag indicates that a vertex starts a new triangle or line strip.
+ */
+ static final int RESTART = 1 ;
+
+ /**
+ * This flag indicates that the next triangle in the strip is defined by
+ * replacing the middle vertex of the previous triangle in the strip.
+ * Equivalent to REPLACE_OLDEST for line strips.
+ */
+ static final int REPLACE_MIDDLE = 2 ;
+
+ /**
+ * This flag indicates that the next triangle in the strip is defined by
+ * replacing the oldest vertex of the previous triangle in the strip.
+ * Equivalent to REPLACE_MIDDLE for line strips.
+ */
+ static final int REPLACE_OLDEST = 3 ;
+
+ /**
+ * This flag indicates that a vertex is to be pushed into the mesh buffer.
+ */
+ static final int MESH_PUSH = 1 ;
+
+ /**
+ * This flag indicates that a vertex does not use the mesh buffer.
+ */
+ static final int NO_MESH_PUSH = 0 ;
+
+ /**
+ * Byte to float scale factor for scaling byte color components.
+ */
+ static final float ByteToFloatScale = 1.0f/255.0f;
+
+ /**
+ * Type of this stream, either CompressedGeometryHeader.POINT_BUFFER,
+ * CompressedGeometryHeader.LINE_BUFFER, or
+ * CompressedGeometryHeader.TRIANGLE_BUFFER
+ */
+ int streamType ;
+
+ /**
+ * A mask indicating which components are present in each vertex, as
+ * defined by GeometryArray.
+ */
+ int vertexComponents ;
+
+ /**
+ * Boolean indicating colors are bundled with the vertices.
+ */
+ boolean vertexColors ;
+
+ /**
+ * Boolean indicating RGB colors are bundled with the vertices.
+ */
+ boolean vertexColor3 ;
+
+ /**
+ * Boolean indicating RGBA colors are bundled with the vertices.
+ */
+ boolean vertexColor4 ;
+
+ /**
+ * Boolean indicating normals are bundled with the vertices.
+ */
+ boolean vertexNormals ;
+
+ /**
+ * Boolean indicating texture coordinates are present.
+ */
+ boolean vertexTextures ;
+
+ /**
+ * Boolean indicating that 2D texture coordinates are used.
+ * Currently only used to skip over textures in interleaved data.
+ */
+ boolean vertexTexture2 ;
+
+ /**
+ * Boolean indicating that 3D texture coordinates are used.
+ * Currently only used to skip over textures in interleaved data.
+ */
+ boolean vertexTexture3 ;
+
+ /**
+ * Boolean indicating that 4D texture coordinates are used.
+ * Currently only used to skip over textures in interleaved data.
+ */
+ boolean vertexTexture4 ;
+
+ /**
+ * Axes-aligned box enclosing all vertices in model coordinates.
+ */
+ Point3d mcBounds[] = new Point3d[2] ;
+
+ /**
+ * Axes-aligned box enclosing all vertices in normalized coordinates.
+ */
+ Point3d ncBounds[] = new Point3d[2] ;
+
+ /**
+ * Axes-aligned box enclosing all vertices in quantized coordinates.
+ */
+ Point3i qcBounds[] = new Point3i[2] ;
+
+ /**
+ * Center for normalizing positions to the unit cube.
+ */
+ double center[] = new double[3] ;
+
+ /**
+ * Maximum position range along the 3 axes.
+ */
+ double positionRangeMaximum ;
+
+ /**
+ * Scale for normalizing positions to the unit cube.
+ */
+ double scale ;
+
+ /**
+ * Current position component (X, Y, and Z) quantization value. This can
+ * range from 1 to 16 bits and has a default of 16.<p>
+ *
+ * At 1 bit of quantization it is not possible to express positive
+ * absolute or delta positions.
+ */
+ int positionQuant ;
+
+ /**
+ * Current color component (R, G, B, A) quantization value. This can
+ * range from 2 to 16 bits and has a default of 9.<p>
+ *
+ * A color component is represented with a signed fixed-point value in
+ * order to be able express negative deltas; the default of 9 bits
+ * corresponds to the 8-bit color component range of the graphics hardware
+ * commonly available. Colors must be non-negative, so the lower limit of
+ * quantization is 2 bits.
+ */
+ int colorQuant ;
+
+ /**
+ * Current normal component (U and V) quantization value. This can range
+ * from 0 to 6 bits and has a default of 6.<p>
+ *
+ * At 0 bits of quantization normals are represented only as 6 bit
+ * sextant/octant pairs and 14 specially encoded normals (the 6 axis
+ * normals and the 8 octant midpoint normals); since U and V can only be 0
+ * at the minimum quantization, the totally number of unique normals is
+ * 12 + 14 = 26.
+ */
+ int normalQuant ;
+
+ /**
+ * Flag indicating position quantization change.
+ */
+ boolean positionQuantChanged ;
+
+ /**
+ * Flag indicating color quantization change.
+ */
+ boolean colorQuantChanged ;
+
+ /**
+ * Flag indicating normal quantization change.
+ */
+ boolean normalQuantChanged ;
+
+ /**
+ * Last quantized position.
+ */
+ int lastPosition[] = new int[3] ;
+
+ /**
+ * Last quantized color.
+ */
+ int lastColor[] = new int[4] ;
+
+ /**
+ * Last quantized normal's sextant.
+ */
+ int lastSextant ;
+
+ /**
+ * Last quantized normal's octant.
+ */
+ int lastOctant ;
+
+ /**
+ * Last quantized normal's U encoding parameter.
+ */
+ int lastU ;
+
+ /**
+ * Last quantized normal's V encoding parameter.
+ */
+ int lastV ;
+
+ /**
+ * Flag indicating last normal used a special encoding.
+ */
+ boolean lastSpecialNormal ;
+
+ /**
+ * Flag indicating the first position in this stream.
+ */
+ boolean firstPosition ;
+
+ /**
+ * Flag indicating the first color in this stream.
+ */
+ boolean firstColor ;
+
+ /**
+ * Flag indicating the first normal in this stream.
+ */
+ boolean firstNormal ;
+
+ /**
+ * The total number of bytes used to create the uncompressed geometric
+ * elements in this stream, useful for performance analysis. This
+ * excludes mesh buffer references.
+ */
+ int byteCount ;
+
+ /**
+ * The number of vertices created for this stream, excluding mesh buffer
+ * references.
+ */
+ int vertexCount ;
+
+ /**
+ * The number of mesh buffer references created for this stream.
+ */
+ int meshReferenceCount ;
+
+ /**
+ * Mesh buffer mirror used for computing deltas during quantization pass
+ * and a limited meshing algorithm for unstripped data.
+ */
+ MeshBuffer meshBuffer = new MeshBuffer() ;
+
+
+ // Collection which holds the elements of this stream.
+ private Collection stream ;
+
+ // True if preceding stream elements were colors or normals. Used to flag
+ // color and normal mesh buffer substitution when computing deltas during
+ // quantization pass.
+ private boolean lastElementColor = false ;
+ private boolean lastLastElementColor = false ;
+ private boolean lastElementNormal = false ;
+ private boolean lastLastElementNormal = false ;
+
+ // Some convenient temporary holding variables.
+ private Point3f p3f = new Point3f() ;
+ private Color3f c3f = new Color3f() ;
+ private Color4f c4f = new Color4f() ;
+ private Vector3f n3f = new Vector3f() ;
+
+
+ // Private constructor for common initializations.
+ private CompressionStream() {
+ this.stream = new LinkedList() ;
+
+ byteCount = 0 ;
+ vertexCount = 0 ;
+ meshReferenceCount = 0 ;
+
+ mcBounds[0] = new Point3d(Double.POSITIVE_INFINITY,
+ Double.POSITIVE_INFINITY,
+ Double.POSITIVE_INFINITY) ;
+ mcBounds[1] = new Point3d(Double.NEGATIVE_INFINITY,
+ Double.NEGATIVE_INFINITY,
+ Double.NEGATIVE_INFINITY) ;
+
+ qcBounds[0] = new Point3i(Integer.MAX_VALUE,
+ Integer.MAX_VALUE,
+ Integer.MAX_VALUE) ;
+ qcBounds[1] = new Point3i(Integer.MIN_VALUE,
+ Integer.MIN_VALUE,
+ Integer.MIN_VALUE) ;
+
+ /* normalized bounds computed from quantized bounds */
+ ncBounds[0] = new Point3d() ;
+ ncBounds[1] = new Point3d() ;
+ }
+
+ /**
+ * Creates a new CompressionStream for the specified geometry type and
+ * vertex format.<p>
+ *
+ * @param streamType type of data in this stream, either
+ * CompressedGeometryHeader.POINT_BUFFER,
+ * CompressedGeometryHeader.LINE_BUFFER, or
+ * CompressedGeometryHeader.TRIANGLE_BUFFER
+ *
+ * @param vertexComponents a mask indicating which components are present
+ * in each vertex, as defined by GeometryArray: COORDINATES, NORMALS, and
+ * COLOR_3 or COLOR_4.
+ *
+ * @see GeometryCompressor
+ * @see GeometryArray
+ */
+ CompressionStream(int streamType, int vertexComponents) {
+ this() ;
+ this.streamType = streamType ;
+ this.vertexComponents = getVertexComponents(vertexComponents) ;
+ }
+
+ // See what vertex geometry components are present. The byReference,
+ // interleaved, useNIOBuffer, and useCoordIndexOnly flags are not
+ // examined.
+ private int getVertexComponents(int vertexFormat) {
+ int components = 0 ;
+
+ vertexColors = vertexColor3 = vertexColor4 = vertexNormals =
+ vertexTextures = vertexTexture2 = vertexTexture3 = vertexTexture4 =
+ false ;
+
+ if ((vertexFormat & GeometryArray.NORMALS) != 0) {
+ vertexNormals = true ;
+ components &= GeometryArray.NORMALS ;
+ if (debug) System.out.println("vertexNormals") ;
+ }
+
+ if ((vertexFormat & GeometryArray.COLOR_3) != 0) {
+ vertexColors = true ;
+
+ if ((vertexFormat & GeometryArray.COLOR_4) != 0) {
+ vertexColor4 = true ;
+ components &= GeometryArray.COLOR_4 ;
+ if (debug) System.out.println("vertexColor4") ;
+ }
+ else {
+ vertexColor3 = true ;
+ components &= GeometryArray.COLOR_3 ;
+ if (debug) System.out.println("vertexColor3") ;
+ }
+ }
+
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
+ vertexTextures = true ;
+ vertexTexture2 = true ;
+ components &= GeometryArray.TEXTURE_COORDINATE_2 ;
+ if (debug) System.out.println("vertexTexture2") ;
+ }
+ else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
+ vertexTextures = true ;
+ vertexTexture3 = true ;
+ components &= GeometryArray.TEXTURE_COORDINATE_3 ;
+ if (debug) System.out.println("vertexTexture3") ;
+ }
+ else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
+ vertexTextures = true ;
+ vertexTexture4 = true ;
+ components &= GeometryArray.TEXTURE_COORDINATE_4 ;
+ if (debug) System.out.println("vertexTexture4") ;
+ }
+
+ if (vertexTextures)
+ // Throw exception for now until texture is supported.
+ throw new UnsupportedOperationException
+ ("\ncompression of texture coordinates is not supported") ;
+
+ return components ;
+ }
+
+ // Get the streamType associated with a GeometryArray instance.
+ private int getStreamType(GeometryArray ga) {
+ if (ga instanceof TriangleStripArray ||
+ ga instanceof IndexedTriangleStripArray ||
+ ga instanceof TriangleFanArray ||
+ ga instanceof IndexedTriangleFanArray ||
+ ga instanceof TriangleArray ||
+ ga instanceof IndexedTriangleArray ||
+ ga instanceof QuadArray ||
+ ga instanceof IndexedQuadArray)
+
+ return CompressedGeometryHeader.TRIANGLE_BUFFER ;
+
+ else if (ga instanceof LineArray ||
+ ga instanceof IndexedLineArray ||
+ ga instanceof LineStripArray ||
+ ga instanceof IndexedLineStripArray)
+
+ return CompressedGeometryHeader.LINE_BUFFER ;
+
+ else
+ return CompressedGeometryHeader.POINT_BUFFER ;
+ }
+
+ /**
+ * Iterates across all compression stream elements and applies
+ * quantization parameters, encoding consecutive vertices as delta values
+ * whenever possible. Each geometric element is mapped to a HuffmanNode
+ * object containing its resulting bit length, right shift (trailing 0
+ * count), and absolute or relative status.<p>
+ *
+ * Positions are normalized to span a unit cube via an offset and a
+ * uniform scale factor that maps the midpoint of the object extents along
+ * each dimension to the origin, and the longest dimension of the object to
+ * the open interval (-1.0 .. +1.0). The geometric endpoints along that
+ * dimension are both one quantum away from unity; for example, at a
+ * position quantization of 6 bits, an object would be normalized so that
+ * its most negative dimension is at (-1 + 1/64) and the most positive is
+ * at (1 - 1/64).<p>
+ *
+ * Normals are assumed to be of unit length. Color components are clamped
+ * to the [0..1) range, where the right endpoint is one quantum less
+ * than 1.0.<p>
+ *
+ * @param huffmanTable Table which will map geometric compression stream
+ * elements to HuffmanNode objects describing each element's data
+ * representation. This table can then be processed with Huffman's
+ * algorithm to optimize the bit length of descriptor tags according to
+ * the number of geometric elements mapped to each tag.
+ */
+ void quantize(HuffmanTable huffmanTable) {
+ // Set up default initial quantization parameters. The position and
+ // color parameters specify the number of bits for each X, Y, Z, R, G,
+ // B, or A component. The normal quantization parameter specifies the
+ // number of bits for each U and V component.
+ positionQuant = 16 ;
+ colorQuant = 9 ;
+ normalQuant = 6 ;
+
+ // Compute position center and scaling for normalization to the unit
+ // cube. This is a volume bounded by the open intervals (-1..1) on
+ // each axis.
+ center[0] = (mcBounds[1].x + mcBounds[0].x) / 2.0 ;
+ center[1] = (mcBounds[1].y + mcBounds[0].y) / 2.0 ;
+ center[2] = (mcBounds[1].z + mcBounds[0].z) / 2.0 ;
+
+ double xRange = mcBounds[1].x - mcBounds[0].x ;
+ double yRange = mcBounds[1].y - mcBounds[0].y ;
+ double zRange = mcBounds[1].z - mcBounds[0].z ;
+
+ if (xRange > yRange)
+ positionRangeMaximum = xRange ;
+ else
+ positionRangeMaximum = yRange ;
+
+ if (zRange > positionRangeMaximum)
+ positionRangeMaximum = zRange ;
+
+ // Adjust the range of the unit cube to match the default
+ // quantization.
+ //
+ // This scale factor along with the center values computed above will
+ // produce 16-bit integer representations of the floating point
+ // position coordinates ranging symmetrically about 0 from -32767 to
+ // +32767. -32768 is not used and the normalized floating point
+ // position coordinates of -1.0 as well as +1.0 will not be
+ // represented.
+ //
+ // Applications which wish to seamlessly stitch together compressed
+ // objects will need to be aware that the range of normalized
+ // positions will be one quantum away from the [-1..1] endpoints of
+ // the unit cube and should adjust scale factors accordingly.
+ scale = (2.0 / positionRangeMaximum) * (32767.0 / 32768.0) ;
+
+ // Flag quantization change.
+ positionQuantChanged = colorQuantChanged = normalQuantChanged = true ;
+
+ // Flag first position, color, and normal.
+ firstPosition = firstColor = firstNormal = true ;
+
+ // Apply quantization.
+ Iterator i = stream.iterator() ;
+ while (i.hasNext()) {
+ Object o = i.next() ;
+
+ if (o instanceof CompressionStreamElement) {
+ ((CompressionStreamElement)o).quantize(this, huffmanTable) ;
+
+ // Keep track of whether last two elements were colors or
+ // normals for mesh buffer component substitution semantics.
+ lastLastElementColor = lastElementColor ;
+ lastLastElementNormal = lastElementNormal ;
+ lastElementColor = lastElementNormal = false ;
+
+ if (o instanceof CompressionStreamColor)
+ lastElementColor = true ;
+ else if (o instanceof CompressionStreamNormal)
+ lastElementNormal = true ;
+ }
+ }
+
+ // Compute the bounds in normalized coordinates.
+ ncBounds[0].x = (double)qcBounds[0].x / 32768.0 ;
+ ncBounds[0].y = (double)qcBounds[0].y / 32768.0 ;
+ ncBounds[0].z = (double)qcBounds[0].z / 32768.0 ;
+
+ ncBounds[1].x = (double)qcBounds[1].x / 32768.0 ;
+ ncBounds[1].y = (double)qcBounds[1].y / 32768.0 ;
+ ncBounds[1].z = (double)qcBounds[1].z / 32768.0 ;
+ }
+
+ /**
+ * Iterates across all compression stream elements and builds the
+ * compressed geometry command stream output.<p>
+ *
+ * @param huffmanTable Table which maps geometric elements in this stream
+ * to tags describing the encoding parameters (length, shift, and
+ * absolute/relative status) to be used for their representations in the
+ * compressed output. All tags must be 6 bits or less in length, and the
+ * sum of the number of bits in the tag plus the number of bits in the
+ * data it describes must be at least 6 bits in length.
+ *
+ * @param outputBuffer CommandStream to use for collecting the compressed
+ * bits.
+ */
+ void outputCommands(HuffmanTable huffmanTable, CommandStream outputBuffer) {
+ //
+ // The first command output is setState to indicate what data is
+ // bundled with each vertex. Although the semantics of geometry
+ // decompression allow setState to appear anywhere in the stream, this
+ // cannot be handled by the current Java 3D software decompressor,
+ // which internally decompresses an entire compressed buffer into a
+ // single retained object sharing a single consistent vertex format.
+ // This limitation may be removed in subsequent releases of Java 3D.
+ //
+ int bnv = (vertexNormals? 1 : 0) ;
+ int bcv = ((vertexColor3 || vertexColor4)? 1 : 0) ;
+ int cap = (vertexColor4? 1 : 0) ;
+
+ int command = CommandStream.SET_STATE | bnv ;
+ long data = (bcv << 2) | (cap << 1) ;
+
+ // Output the setState command.
+ outputBuffer.addCommand(command, 8, data, 3) ;
+
+ // Output the Huffman table commands.
+ huffmanTable.outputCommands(outputBuffer) ;
+
+ // Output each compression stream element's data.
+ Iterator i = stream.iterator() ;
+ while (i.hasNext()) {
+ Object o = i.next() ;
+ if (o instanceof CompressionStreamElement)
+ ((CompressionStreamElement)o).outputCommand(huffmanTable,
+ outputBuffer) ;
+ }
+
+ // Finish the header-forwarding interleave and long-word align.
+ outputBuffer.end() ;
+ }
+
+ /**
+ * Retrieve the total size of the uncompressed geometric data in bytes,
+ * excluding mesh buffer references.
+ * @return uncompressed byte count
+ */
+ int getByteCount() {
+ return byteCount ;
+ }
+
+ /**
+ * Retrieve the the number of vertices created for this stream, excluding
+ * mesh buffer references.
+ * @return vertex count
+ */
+ int getVertexCount() {
+ return vertexCount ;
+ }
+
+ /**
+ * Retrieve the number of mesh buffer references created for this stream.
+ * @return mesh buffer reference count
+ */
+ int getMeshReferenceCount() {
+ return meshReferenceCount ;
+ }
+
+ /**
+ * Stream element that sets position quantization during quantize pass.
+ */
+ private class PositionQuant extends CompressionStreamElement {
+ int value ;
+
+ PositionQuant(int value) {
+ this.value = value ;
+ }
+
+ void quantize(CompressionStream s, HuffmanTable t) {
+ positionQuant = value ;
+ positionQuantChanged = true ;
+
+ // Adjust range of unit cube scaling to match quantization.
+ scale = (2.0 / positionRangeMaximum) *
+ (((double)((1 << (value-1)) - 1))/((double)(1 << (value-1)))) ;
+ }
+
+ public String toString() {
+ return "positionQuant: " + value ;
+ }
+ }
+
+ /**
+ * Stream element that sets normal quantization during quantize pass.
+ */
+ private class NormalQuant extends CompressionStreamElement {
+ int value ;
+
+ NormalQuant(int value) {
+ this.value = value ;
+ }
+
+ void quantize(CompressionStream s, HuffmanTable t) {
+ normalQuant = value ;
+ normalQuantChanged = true ;
+ }
+
+ public String toString() {
+ return "normalQuant: " + value ;
+ }
+ }
+
+ /**
+ * Stream element that sets color quantization during quantize pass.
+ */
+ private class ColorQuant extends CompressionStreamElement {
+ int value ;
+
+ ColorQuant(int value) {
+ this.value = value ;
+ }
+
+ void quantize(CompressionStream s, HuffmanTable t) {
+ colorQuant = value ;
+ colorQuantChanged = true ;
+ }
+
+ public String toString() {
+ return "colorQuant: " + value ;
+ }
+ }
+
+ /**
+ * Stream element that references the mesh buffer.
+ */
+ private class MeshReference extends CompressionStreamElement {
+ int stripFlag, meshIndex ;
+
+ MeshReference(int stripFlag, int meshIndex) {
+ this.stripFlag = stripFlag ;
+ this.meshIndex = meshIndex ;
+ meshReferenceCount++ ;
+ }
+
+ void quantize(CompressionStream s, HuffmanTable t) {
+ // Retrieve the vertex from the mesh buffer mirror and set up the
+ // data needed for the next stream element to compute its deltas.
+ CompressionStreamVertex v = meshBuffer.getVertex(meshIndex) ;
+ lastPosition[0] = v.xAbsolute ;
+ lastPosition[1] = v.yAbsolute ;
+ lastPosition[2] = v.zAbsolute ;
+
+ // Set up last color data if it exists and previous elements
+ // don't override it.
+ if (v.color != null && !lastElementColor &&
+ !(lastElementNormal && lastLastElementColor)) {
+ lastColor[0] = v.color.rAbsolute ;
+ lastColor[1] = v.color.gAbsolute ;
+ lastColor[2] = v.color.bAbsolute ;
+ lastColor[3] = v.color.aAbsolute ;
+ }
+
+ // Set up last normal data if it exists and previous element
+ // doesn't override it.
+ if (v.normal != null && !lastElementNormal &&
+ !(lastElementColor && lastLastElementNormal)) {
+ lastSextant = v.normal.sextant ;
+ lastOctant = v.normal.octant ;
+ lastU = v.normal.uAbsolute ;
+ lastV = v.normal.vAbsolute ;
+ lastSpecialNormal = v.normal.specialNormal ;
+ }
+ }
+
+ void outputCommand(HuffmanTable t, CommandStream outputBuffer) {
+ int command = CommandStream.MESH_B_R ;
+ long data = stripFlag & 0x1 ;
+
+ command |= (((meshIndex & 0xf) << 1) | (stripFlag >> 1)) ;
+ outputBuffer.addCommand(command, 8, data, 1) ;
+ }
+
+ public String toString() {
+ return
+ "meshReference: stripFlag " + stripFlag +
+ " meshIndex " + meshIndex ;
+ }
+ }
+
+
+ /**
+ * Copy vertex data and add it to the end of this stream.
+ * @param pos position data
+ * @param stripFlag vertex replacement flag, either RESTART,
+ * REPLACE_OLDEST, or REPLACE_MIDDLE
+ */
+ void addVertex(Point3f pos, int stripFlag) {
+ stream.add(new CompressionStreamVertex(this, pos,
+ (Vector3f)null, (Color3f)null,
+ stripFlag, NO_MESH_PUSH)) ;
+ }
+
+ /**
+ * Copy vertex data and add it to the end of this stream.
+ * @param pos position data
+ * @param norm normal data
+ * @param stripFlag vertex replacement flag, either RESTART,
+ * REPLACE_OLDEST, or REPLACE_MIDDLE
+ */
+ void addVertex(Point3f pos, Vector3f norm, int stripFlag) {
+ stream.add(new CompressionStreamVertex
+ (this, pos, norm, (Color3f)null, stripFlag, NO_MESH_PUSH)) ;
+ }
+
+ /**
+ * Copy vertex data and add it to the end of this stream.
+ * @param pos position data
+ * @param color color data
+ * @param stripFlag vertex replacement flag, either RESTART,
+ * REPLACE_OLDEST, or REPLACE_MIDDLE
+ */
+ void addVertex(Point3f pos, Color3f color, int stripFlag) {
+ stream.add(new CompressionStreamVertex
+ (this, pos, (Vector3f)null, color, stripFlag, NO_MESH_PUSH)) ;
+ }
+
+ /**
+ * Copy vertex data and add it to the end of this stream.
+ * @param pos position data
+ * @param color color data
+ * @param stripFlag vertex replacement flag, either RESTART,
+ * REPLACE_OLDEST, or REPLACE_MIDDLE
+ */
+ void addVertex(Point3f pos, Color4f color, int stripFlag) {
+ stream.add(new CompressionStreamVertex
+ (this, pos, (Vector3f)null, color, stripFlag, NO_MESH_PUSH)) ;
+ }
+
+ /**
+ * Copy vertex data and add it to the end of this stream.
+ * @param pos position data
+ * @param norm normal data
+ * @param color color data
+ * @param stripFlag vertex replacement flag, either RESTART,
+ * REPLACE_OLDEST, or REPLACE_MIDDLE
+ */
+ void addVertex(Point3f pos, Vector3f norm, Color3f color,
+ int stripFlag) {
+ stream.add(new CompressionStreamVertex
+ (this, pos, norm, color, stripFlag, NO_MESH_PUSH)) ;
+ }
+
+ /**
+ * Copy vertex data and add it to the end of this stream.
+ * @param pos position data
+ * @param norm normal data
+ * @param color color data
+ * @param stripFlag vertex replacement flag, either RESTART,
+ * REPLACE_OLDEST, or REPLACE_MIDDLE
+ */
+ void addVertex(Point3f pos, Vector3f norm, Color4f color,
+ int stripFlag) {
+ stream.add(new CompressionStreamVertex
+ (this, pos, norm, color, stripFlag, NO_MESH_PUSH)) ;
+ }
+
+ /**
+ * Copy vertex data and add it to the end of this stream.
+ * @param pos position data
+ * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
+ * or REPLACE_MIDDLE
+ * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
+ */
+ void addVertex(Point3f pos, int stripFlag, int meshFlag) {
+ stream.add(new CompressionStreamVertex
+ (this, pos, (Vector3f)null, (Color3f)null, stripFlag, meshFlag)) ;
+ }
+
+ /**
+ * Copy vertex data and add it to the end of this stream.
+ * @param pos position data
+ * @param norm normal data
+ * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
+ * or REPLACE_MIDDLE
+ * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
+ */
+ void addVertex(Point3f pos, Vector3f norm,
+ int stripFlag, int meshFlag) {
+ stream.add(new CompressionStreamVertex
+ (this, pos, norm, (Color3f)null, stripFlag, meshFlag)) ;
+ }
+
+ /**
+ * Copy vertex data and add it to the end of this stream.
+ * @param pos position data
+ * @param color color data
+ * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
+ * or REPLACE_MIDDLE
+ * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
+ */
+ void addVertex(Point3f pos, Color3f color,
+ int stripFlag, int meshFlag) {
+ stream.add(new CompressionStreamVertex
+ (this, pos, (Vector3f)null, color, stripFlag, meshFlag)) ;
+ }
+
+ /**
+ * Copy vertex data and add it to the end of this stream.
+ * @param pos position data
+ * @param color color data
+ * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
+ * or REPLACE_MIDDLE
+ * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
+ */
+ void addVertex(Point3f pos, Color4f color,
+ int stripFlag, int meshFlag) {
+ stream.add(new CompressionStreamVertex
+ (this, pos, (Vector3f)null, color, stripFlag, meshFlag)) ;
+ }
+
+ /**
+ * Copy vertex data and add it to the end of this stream.
+ * @param pos position data
+ * @param norm normal data
+ * @param color color data
+ * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
+ * or REPLACE_MIDDLE
+ * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
+ */
+ void addVertex(Point3f pos, Vector3f norm, Color3f color,
+ int stripFlag, int meshFlag) {
+ stream.add(new CompressionStreamVertex
+ (this, pos, norm, color, stripFlag, meshFlag)) ;
+ }
+
+ /**
+ * Copy vertex data and add it to the end of this stream.
+ * @param pos position data
+ * @param norm normal data
+ * @param color color data
+ * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
+ * or REPLACE_MIDDLE
+ * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
+ */
+ void addVertex(Point3f pos, Vector3f norm, Color4f color,
+ int stripFlag, int meshFlag) {
+ stream.add(new CompressionStreamVertex
+ (this, pos, norm, color, stripFlag, meshFlag)) ;
+ }
+
+ /**
+ * Copy vertex data and add it to the end of this stream.
+ * @param pos position data
+ * @param norm normal data
+ * @param color color data, either Color3f or Color4f, determined by
+ * current vertex format
+ * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
+ * or REPLACE_MIDDLE
+ * @param meshFlag if MESH_PUSH the vertex is pushed into the mesh buffer
+ */
+ void addVertex(Point3f pos, Vector3f norm,
+ Object color, int stripFlag, int meshFlag) {
+
+ if (vertexColor3)
+ stream.add(new CompressionStreamVertex
+ (this, pos, norm, (Color3f)color, stripFlag, meshFlag)) ;
+ else
+ stream.add(new CompressionStreamVertex
+ (this, pos, norm, (Color4f)color, stripFlag, meshFlag)) ;
+ }
+
+ /**
+ * Add a mesh buffer reference to this stream.
+ * @param stripFlag vertex replacement flag, either RESTART, REPLACE_OLDEST,
+ * or REPLACE_MIDDLE
+ * @param meshIndex index of vertex to retrieve from the mesh buffer
+ */
+ void addMeshReference(int stripFlag, int meshIndex) {
+ stream.add(new MeshReference(stripFlag, meshIndex)) ;
+ }
+
+ /**
+ * Copy the given color to the end of this stream and use it as a global
+ * state change that applies to all subsequent vertices.
+ */
+ void addColor(Color3f c3f) {
+ stream.add(new CompressionStreamColor(this, c3f)) ;
+ }
+
+ /**
+ * Copy the given color to the end of this stream and use it as a global
+ * state change that applies to all subsequent vertices.
+ */
+ void addColor(Color4f c4f) {
+ stream.add(new CompressionStreamColor(this, c4f)) ;
+ }
+
+ /**
+ * Copy the given normal to the end of this stream and use it as a global
+ * state change that applies to all subsequent vertices.
+ */
+ void addNormal(Vector3f n) {
+ stream.add(new CompressionStreamNormal(this, n)) ;
+ }
+
+ /**
+ * Add a new position quantization value to the end of this stream that
+ * will apply to all subsequent vertex positions.
+ *
+ * @param value number of bits to quantize each position's X, Y,
+ * and Z components, ranging from 1 to 16 with a default of 16
+ */
+ void addPositionQuantization(int value) {
+ stream.add(new PositionQuant(value)) ;
+ }
+
+ /**
+ * Add a new color quantization value to the end of this stream that will
+ * apply to all subsequent colors.
+ *
+ * @param value number of bits to quantize each color's R, G, B, and
+ * alpha components, ranging from 2 to 16 with a default of 9
+ */
+ void addColorQuantization(int value) {
+ stream.add(new ColorQuant(value)) ;
+ }
+
+ /**
+ * Add a new normal quantization value to the end of this stream that will
+ * apply to all subsequent normals. This value specifies the number of
+ * bits for each normal's U and V components.
+ *
+ * @param value number of bits for quantizing U and V, ranging from 0 to
+ * 6 with a default of 6
+ */
+ void addNormalQuantization(int value) {
+ stream.add(new NormalQuant(value)) ;
+ }
+
+ /**
+ * Interface to access GeometryArray vertex components and add them to the
+ * compression stream.
+ *
+ * A processVertex() implementation retrieves vertex components using the
+ * appropriate access semantics of a particular GeometryArray, and adds
+ * them to the compression stream.
+ *
+ * The implementation always pushes vertices into the mesh buffer unless
+ * they match ones already there; if they do, it generates mesh buffer
+ * references instead. This reduces the number of vertices when
+ * non-stripped abutting facets are added to the stream.
+ *
+ * Note: Level II geometry compression semantics allow the mesh buffer
+ * normals to be substituted with the value of an immediately
+ * preceding SetNormal command, but this is unavailable in Level I.
+ *
+ * @param index vertex offset from the beginning of its data array
+ * @param stripFlag RESTART, REPLACE_MIDDLE, or REPLACE_OLDEST
+ */
+ private interface GeometryAccessor {
+ void processVertex(int index, int stripFlag) ;
+ }
+
+ /**
+ * This class implements the GeometryAccessor interface for geometry
+ * arrays accessed with by-copy semantics.
+ */
+ private class ByCopyGeometry implements GeometryAccessor {
+ Point3f[] positions = null ;
+ Vector3f[] normals = null ;
+ Color3f[] colors3 = null ;
+ Color4f[] colors4 = null ;
+
+ ByCopyGeometry(GeometryArray ga) {
+ this(ga, ga.getInitialVertexIndex(), ga.getValidVertexCount()) ;
+ }
+
+ ByCopyGeometry(GeometryArray ga,
+ int firstVertex, int validVertexCount) {
+ int i ;
+ positions = new Point3f[validVertexCount] ;
+ for (i = 0 ; i < validVertexCount ; i++)
+ positions[i] = new Point3f() ;
+
+ ga.getCoordinates(firstVertex, positions) ;
+
+ if (vertexNormals) {
+ normals = new Vector3f[validVertexCount] ;
+ for (i = 0 ; i < validVertexCount ; i++)
+ normals[i] = new Vector3f() ;
+
+ ga.getNormals(firstVertex, normals) ;
+ }
+
+ if (vertexColor3) {
+ colors3 = new Color3f[validVertexCount] ;
+ for (i = 0 ; i < validVertexCount ; i++)
+ colors3[i] = new Color3f() ;
+
+ ga.getColors(firstVertex, colors3) ;
+ }
+ else if (vertexColor4) {
+ colors4 = new Color4f[validVertexCount] ;
+ for (i = 0 ; i < validVertexCount ; i++)
+ colors4[i] = new Color4f() ;
+
+ ga.getColors(firstVertex, colors4) ;
+ }
+ }
+
+ public void processVertex(int v, int stripFlag) {
+ Point3f p = positions[v] ;
+ int r = meshBuffer.getMeshReference(p) ;
+
+ if ((r == meshBuffer.NOT_FOUND) ||
+ (vertexNormals && noMeshNormalSubstitution &&
+ (! normals[v].equals(meshBuffer.getNormal(r))))) {
+
+ Vector3f n = vertexNormals? normals[v] : null ;
+ Object c = vertexColor3? (Object)colors3[v] :
+ vertexColor4? (Object)colors4[v] : null ;
+
+ addVertex(p, n, c, stripFlag, MESH_PUSH) ;
+ meshBuffer.push(p, c, n) ;
+ }
+ else {
+ if (vertexNormals && !noMeshNormalSubstitution &&
+ (! normals[v].equals(meshBuffer.getNormal(r))))
+ addNormal(normals[v]) ;
+
+ if (vertexColor3 &&
+ (! colors3[v].equals(meshBuffer.getColor3(r))))
+ addColor(colors3[v]) ;
+
+ else if (vertexColor4 &&
+ (! colors4[v].equals(meshBuffer.getColor4(r))))
+ addColor(colors4[v]) ;
+
+ addMeshReference(stripFlag, r) ;
+ }
+ }
+ }
+
+ /**
+ * Class which holds index array references for a geometry array.
+ */
+ private static class IndexArrays {
+ int colorIndices[] = null ;
+ int normalIndices[] = null ;
+ int positionIndices[] = null ;
+ }
+
+ /**
+ * Retrieves index array references for the specified IndexedGeometryArray.
+ * Index arrays are copied starting from initialIndexIndex.
+ */
+ private void getIndexArrays(GeometryArray ga, IndexArrays ia) {
+ IndexedGeometryArray iga = (IndexedGeometryArray)ga ;
+
+ int initialIndexIndex = iga.getInitialIndexIndex() ;
+ int indexCount = iga.getValidIndexCount() ;
+ int vertexFormat = iga.getVertexFormat() ;
+
+ boolean useCoordIndexOnly = false ;
+ if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) {
+ if (debug) System.out.println("useCoordIndexOnly") ;
+ useCoordIndexOnly = true ;
+ }
+
+ ia.positionIndices = new int[indexCount] ;
+ iga.getCoordinateIndices(initialIndexIndex, ia.positionIndices) ;
+
+ if (vertexNormals) {
+ if (useCoordIndexOnly) {
+ ia.normalIndices = ia.positionIndices ;
+ }
+ else {
+ ia.normalIndices = new int[indexCount] ;
+ iga.getNormalIndices(initialIndexIndex, ia.normalIndices) ;
+ }
+ }
+ if (vertexColor3 || vertexColor4) {
+ if (useCoordIndexOnly) {
+ ia.colorIndices = ia.positionIndices ;
+ }
+ else {
+ ia.colorIndices = new int[indexCount] ;
+ iga.getColorIndices(initialIndexIndex, ia.colorIndices) ;
+ }
+ }
+ }
+
+ /**
+ * Class which holds indices for a specific vertex of an
+ * IndexedGeometryArray.
+ */
+ private static class VertexIndices {
+ int pi, ni, ci ;
+ }
+
+ /**
+ * Retrieves vertex indices for a specific vertex in an
+ * IndexedGeometryArray.
+ */
+ private void getVertexIndices(int v, IndexArrays ia, VertexIndices vi) {
+ vi.pi = ia.positionIndices[v] ;
+ if (vertexNormals)
+ vi.ni = ia.normalIndices[v] ;
+ if (vertexColors)
+ vi.ci = ia.colorIndices[v] ;
+ }
+
+ /**
+ * This class implements the GeometryAccessor interface for indexed
+ * geometry arrays accessed with by-copy semantics.
+ */
+ private class IndexedByCopyGeometry extends ByCopyGeometry {
+ IndexArrays ia = new IndexArrays() ;
+ VertexIndices vi = new VertexIndices() ;
+
+ IndexedByCopyGeometry(GeometryArray ga) {
+ super(ga, 0, ga.getVertexCount()) ;
+ getIndexArrays(ga, ia) ;
+ }
+
+ public void processVertex(int v, int stripFlag) {
+ getVertexIndices(v, ia, vi) ;
+ int r = meshBuffer.getMeshReference(vi.pi) ;
+
+ if ((r == meshBuffer.NOT_FOUND) ||
+ (vertexNormals && noMeshNormalSubstitution &&
+ (vi.ni != meshBuffer.getNormalIndex(r)))) {
+
+ Point3f p = positions[vi.pi] ;
+ Vector3f n = vertexNormals? normals[vi.ni] : null ;
+ Object c = vertexColor3? (Object)colors3[vi.ci] :
+ vertexColor4? (Object)colors4[vi.ci] : null ;
+
+ addVertex(p, n, c, stripFlag, MESH_PUSH) ;
+ meshBuffer.push(vi.pi, vi.ci, vi.ni) ;
+ }
+ else {
+ if (vertexNormals && !noMeshNormalSubstitution &&
+ vi.ni != meshBuffer.getNormalIndex(r))
+ addNormal(normals[vi.ni]) ;
+
+ if (vertexColor3 && vi.ci != meshBuffer.getColorIndex(r))
+ addColor(colors3[vi.ci]) ;
+
+ else if (vertexColor4 && vi.ci != meshBuffer.getColorIndex(r))
+ addColor(colors4[vi.ci]) ;
+
+ addMeshReference(stripFlag, r) ;
+ }
+ }
+ }
+
+ //
+ // NOTE: For now, copies are made of all GeometryArray vertex components
+ // even when by-reference access is available.
+ //
+ private static class VertexCopy {
+ Object c = null ;
+ Point3f p = null ;
+ Vector3f n = null ;
+ Color3f c3 = null ;
+ Color4f c4 = null ;
+ }
+
+ private void processVertexCopy(VertexCopy vc, int stripFlag) {
+ int r = meshBuffer.getMeshReference(vc.p) ;
+
+ if ((r == meshBuffer.NOT_FOUND) ||
+ (vertexNormals && noMeshNormalSubstitution &&
+ (! vc.n.equals(meshBuffer.getNormal(r))))) {
+
+ addVertex(vc.p, vc.n, vc.c, stripFlag, MESH_PUSH) ;
+ meshBuffer.push(vc.p, vc.c, vc.n) ;
+ }
+ else {
+ if (vertexNormals && !noMeshNormalSubstitution &&
+ (! vc.n.equals(meshBuffer.getNormal(r))))
+ addNormal(vc.n) ;
+
+ if (vertexColor3 && (! vc.c3.equals(meshBuffer.getColor3(r))))
+ addColor(vc.c3) ;
+
+ else if (vertexColor4 && (! vc.c4.equals(meshBuffer.getColor4(r))))
+ addColor(vc.c4) ;
+
+ addMeshReference(stripFlag, r) ;
+ }
+ }
+
+ private void processIndexedVertexCopy(VertexCopy vc,
+ VertexIndices vi,
+ int stripFlag) {
+
+ int r = meshBuffer.getMeshReference(vi.pi) ;
+
+ if ((r == meshBuffer.NOT_FOUND) ||
+ (vertexNormals && noMeshNormalSubstitution &&
+ (vi.ni != meshBuffer.getNormalIndex(r)))) {
+
+ addVertex(vc.p, vc.n, vc.c, stripFlag, MESH_PUSH) ;
+ meshBuffer.push(vi.pi, vi.ci, vi.ni) ;
+ }
+ else {
+ if (vertexNormals && !noMeshNormalSubstitution &&
+ vi.ni != meshBuffer.getNormalIndex(r))
+ addNormal(vc.n) ;
+
+ if (vertexColor3 && vi.ci != meshBuffer.getColorIndex(r))
+ addColor(vc.c3) ;
+
+ else if (vertexColor4 && vi.ci != meshBuffer.getColorIndex(r))
+ addColor(vc.c4) ;
+
+ addMeshReference(stripFlag, r) ;
+ }
+ }
+
+ /**
+ * This abstract class implements the GeometryAccessor interface for
+ * concrete subclasses which handle float and NIO interleaved geometry
+ * arrays.
+ */
+ private abstract class InterleavedGeometry implements GeometryAccessor {
+ VertexCopy vc = new VertexCopy() ;
+
+ int vstride = 0 ;
+ int coffset = 0 ;
+ int noffset = 0 ;
+ int poffset = 0 ;
+ int tstride = 0 ;
+ int tcount = 0 ;
+
+ InterleavedGeometry(GeometryArray ga) {
+ if (vertexTextures) {
+ if (vertexTexture2) tstride = 2 ;
+ else if (vertexTexture3) tstride = 3 ;
+ else if (vertexTexture4) tstride = 4 ;
+
+ tcount = ga.getTexCoordSetCount() ;
+ vstride += tcount * tstride ;
+ }
+
+ if (vertexColors) {
+ coffset = vstride ;
+ if (vertexColor3) vstride += 3 ;
+ else vstride += 4 ;
+ }
+
+ if (vertexNormals) {
+ noffset = vstride ;
+ vstride += 3 ;
+ }
+
+ poffset = vstride ;
+ vstride += 3 ;
+ }
+
+ abstract void copyVertex(int pi, int ni, int ci, VertexCopy vc) ;
+
+ public void processVertex(int v, int stripFlag) {
+ copyVertex(v, v, v, vc) ;
+ processVertexCopy(vc, stripFlag) ;
+ }
+ }
+
+ /**
+ * This class implements the GeometryAccessor interface for float
+ * interleaved geometry arrays.
+ */
+ private class InterleavedGeometryFloat extends InterleavedGeometry {
+ float[] vdata = null ;
+
+ InterleavedGeometryFloat(GeometryArray ga) {
+ super(ga) ;
+ vdata = ga.getInterleavedVertices() ;
+ }
+
+ void copyVertex(int pi, int ni, int ci, VertexCopy vc) {
+ int voffset ;
+ voffset = pi * vstride ;
+ vc.p = new Point3f(vdata[voffset + poffset + 0],
+ vdata[voffset + poffset + 1],
+ vdata[voffset + poffset + 2]) ;
+
+ if (vertexNormals) {
+ voffset = ni * vstride ;
+ vc.n = new Vector3f(vdata[voffset + noffset + 0],
+ vdata[voffset + noffset + 1],
+ vdata[voffset + noffset + 2]) ;
+ }
+ if (vertexColor3) {
+ voffset = ci * vstride ;
+ vc.c3 = new Color3f(vdata[voffset + coffset + 0],
+ vdata[voffset + coffset + 1],
+ vdata[voffset + coffset + 2]) ;
+ vc.c = vc.c3 ;
+ }
+ else if (vertexColor4) {
+ voffset = ci * vstride ;
+ vc.c4 = new Color4f(vdata[voffset + coffset + 0],
+ vdata[voffset + coffset + 1],
+ vdata[voffset + coffset + 2],
+ vdata[voffset + coffset + 3]) ;
+ vc.c = vc.c4 ;
+ }
+ }
+ }
+
+ /**
+ * This class implements the GeometryAccessor interface for indexed
+ * interleaved geometry arrays.
+ */
+ private class IndexedInterleavedGeometryFloat
+ extends InterleavedGeometryFloat {
+
+ IndexArrays ia = new IndexArrays() ;
+ VertexIndices vi = new VertexIndices() ;
+
+ IndexedInterleavedGeometryFloat(GeometryArray ga) {
+ super(ga) ;
+ getIndexArrays(ga, ia) ;
+ }
+
+ public void processVertex(int v, int stripFlag) {
+ getVertexIndices(v, ia, vi) ;
+ copyVertex(vi.pi, vi.ni, vi.ci, vc) ;
+ processIndexedVertexCopy(vc, vi, stripFlag) ;
+ }
+ }
+
+ /**
+ * This class implements the GeometryAccessor interface for
+ * interleaved NIO geometry arrays.
+ */
+ private class InterleavedGeometryNIO extends InterleavedGeometry {
+ FloatBufferWrapper fbw = null ;
+
+ InterleavedGeometryNIO(GeometryArray ga) {
+ super(ga) ;
+ J3DBuffer buffer = ga.getInterleavedVertexBuffer() ;
+ if (BufferWrapper.getBufferType(buffer) ==
+ BufferWrapper.TYPE_FLOAT) {
+ fbw = new FloatBufferWrapper(buffer) ;
+ }
+ else {
+ throw new IllegalArgumentException
+ ("\ninterleaved vertex buffer must be FloatBuffer") ;
+ }
+ }
+
+ void copyVertex(int pi, int ni, int ci, VertexCopy vc) {
+ int voffset ;
+ voffset = pi * vstride ;
+ vc.p = new Point3f(fbw.get(voffset + poffset + 0),
+ fbw.get(voffset + poffset + 1),
+ fbw.get(voffset + poffset + 2)) ;
+
+ if (vertexNormals) {
+ voffset = ni * vstride ;
+ vc.n = new Vector3f(fbw.get(voffset + noffset + 0),
+ fbw.get(voffset + noffset + 1),
+ fbw.get(voffset + noffset + 2)) ;
+ }
+ if (vertexColor3) {
+ voffset = ci * vstride ;
+ vc.c3 = new Color3f(fbw.get(voffset + coffset + 0),
+ fbw.get(voffset + coffset + 1),
+ fbw.get(voffset + coffset + 2)) ;
+ vc.c = vc.c3 ;
+ }
+ else if (vertexColor4) {
+ voffset = ci * vstride ;
+ vc.c4 = new Color4f(fbw.get(voffset + coffset + 0),
+ fbw.get(voffset + coffset + 1),
+ fbw.get(voffset + coffset + 2),
+ fbw.get(voffset + coffset + 3)) ;
+ vc.c = vc.c4 ;
+ }
+ }
+ }
+
+ /**
+ * This class implements the GeometryAccessor interface for indexed
+ * interleaved NIO geometry arrays.
+ */
+ private class IndexedInterleavedGeometryNIO extends InterleavedGeometryNIO {
+ IndexArrays ia = new IndexArrays() ;
+ VertexIndices vi = new VertexIndices() ;
+
+ IndexedInterleavedGeometryNIO(GeometryArray ga) {
+ super(ga) ;
+ getIndexArrays(ga, ia) ;
+ }
+
+ public void processVertex(int v, int stripFlag) {
+ getVertexIndices(v, ia, vi) ;
+ copyVertex(vi.pi, vi.ni, vi.ci, vc) ;
+ processIndexedVertexCopy(vc, vi, stripFlag) ;
+ }
+ }
+
+ /**
+ * This class implements the GeometryAccessor interface for
+ * non-interleaved geometry arrays accessed with by-reference semantics.
+ */
+ private class ByRefGeometry implements GeometryAccessor {
+ VertexCopy vc = new VertexCopy() ;
+
+ byte[] colorsB = null ;
+ float[] colorsF = null ;
+ float[] normals = null ;
+ float[] positionsF = null ;
+ double[] positionsD = null ;
+
+ int initialPositionIndex = 0 ;
+ int initialNormalIndex = 0 ;
+ int initialColorIndex = 0 ;
+
+ ByRefGeometry(GeometryArray ga) {
+ positionsF = ga.getCoordRefFloat() ;
+ if (debug && positionsF != null)
+ System.out.println("float positions") ;
+
+ positionsD = ga.getCoordRefDouble() ;
+ if (debug && positionsD != null)
+ System.out.println("double positions") ;
+
+ if (positionsF == null && positionsD == null)
+ throw new UnsupportedOperationException
+ ("\nby-reference access to Point3{d,f} arrays") ;
+
+ initialPositionIndex = ga.getInitialCoordIndex() ;
+
+ if (vertexColors) {
+ colorsB = ga.getColorRefByte() ;
+ if (debug && colorsB != null)
+ System.out.println("byte colors") ;
+
+ colorsF = ga.getColorRefFloat() ;
+ if (debug && colorsF != null)
+ System.out.println("float colors") ;
+
+ if (colorsB == null && colorsF == null)
+ throw new UnsupportedOperationException
+ ("\nby-reference access to Color{3b,3f,4b,4f} arrays") ;
+
+ initialColorIndex = ga.getInitialColorIndex() ;
+ }
+
+ if (vertexNormals) {
+ normals = ga.getNormalRefFloat() ;
+ if (debug && normals != null)
+ System.out.println("float normals") ;
+
+ if (normals == null)
+ throw new UnsupportedOperationException
+ ("\nby-reference access to Normal3f array") ;
+
+ initialNormalIndex = ga.getInitialNormalIndex() ;
+ }
+ }
+
+ void copyVertex(int pi, int ni, int ci, VertexCopy vc) {
+ pi *= 3 ;
+ if (positionsF != null) {
+ vc.p = new Point3f(positionsF[pi + 0],
+ positionsF[pi + 1],
+ positionsF[pi + 2]) ;
+ }
+ else {
+ vc.p = new Point3f((float)positionsD[pi + 0],
+ (float)positionsD[pi + 1],
+ (float)positionsD[pi + 2]) ;
+ }
+
+ ni *= 3 ;
+ if (vertexNormals) {
+ vc.n = new Vector3f(normals[ni + 0],
+ normals[ni + 1],
+ normals[ni + 2]) ;
+ }
+
+ if (vertexColor3) {
+ ci *= 3 ;
+ if (colorsB != null) {
+ vc.c3 = new Color3f
+ ((colorsB[ci + 0] & 0xff) * ByteToFloatScale,
+ (colorsB[ci + 1] & 0xff) * ByteToFloatScale,
+ (colorsB[ci + 2] & 0xff) * ByteToFloatScale) ;
+ }
+ else {
+ vc.c3 = new Color3f(colorsF[ci + 0],
+ colorsF[ci + 1],
+ colorsF[ci + 2]) ;
+ }
+ vc.c = vc.c3 ;
+ }
+ else if (vertexColor4) {
+ ci *= 4 ;
+ if (colorsB != null) {
+ vc.c4 = new Color4f
+ ((colorsB[ci + 0] & 0xff) * ByteToFloatScale,
+ (colorsB[ci + 1] & 0xff) * ByteToFloatScale,
+ (colorsB[ci + 2] & 0xff) * ByteToFloatScale,
+ (colorsB[ci + 3] & 0xff) * ByteToFloatScale) ;
+ }
+ else {
+ vc.c4 = new Color4f(colorsF[ci + 0],
+ colorsF[ci + 1],
+ colorsF[ci + 2],
+ colorsF[ci + 3]) ;
+ }
+ vc.c = vc.c4 ;
+ }
+ }
+
+ public void processVertex(int v, int stripFlag) {
+ copyVertex(v + initialPositionIndex,
+ v + initialNormalIndex,
+ v + initialColorIndex, vc) ;
+
+ processVertexCopy(vc, stripFlag) ;
+ }
+ }
+
+ /**
+ * This class implements the GeometryAccessor interface for indexed
+ * non-interleaved geometry arrays accessed with by-reference semantics.
+ */
+ private class IndexedByRefGeometry extends ByRefGeometry {
+ IndexArrays ia = new IndexArrays() ;
+ VertexIndices vi = new VertexIndices() ;
+
+ IndexedByRefGeometry(GeometryArray ga) {
+ super(ga) ;
+ getIndexArrays(ga, ia) ;
+ }
+
+ public void processVertex(int v, int stripFlag) {
+ getVertexIndices(v, ia, vi) ;
+ copyVertex(vi.pi, vi.ni, vi.ci, vc) ;
+ processIndexedVertexCopy(vc, vi, stripFlag) ;
+ }
+ }
+
+ /**
+ * This class implements the GeometryAccessor interface for
+ * non-interleaved geometry arrays accessed with NIO.
+ */
+ private class ByRefGeometryNIO implements GeometryAccessor {
+ VertexCopy vc = new VertexCopy() ;
+
+ ByteBufferWrapper colorsB = null ;
+ FloatBufferWrapper colorsF = null ;
+ FloatBufferWrapper normals = null ;
+ FloatBufferWrapper positionsF = null ;
+ DoubleBufferWrapper positionsD = null ;
+
+ int initialPositionIndex = 0 ;
+ int initialNormalIndex = 0 ;
+ int initialColorIndex = 0 ;
+
+ ByRefGeometryNIO(GeometryArray ga) {
+ J3DBuffer buffer ;
+ buffer = ga.getCoordRefBuffer() ;
+ initialPositionIndex = ga.getInitialCoordIndex() ;
+
+ switch (BufferWrapper.getBufferType(buffer)) {
+ case BufferWrapper.TYPE_FLOAT:
+ positionsF = new FloatBufferWrapper(buffer) ;
+ if (debug) System.out.println("float positions buffer") ;
+ break ;
+ case BufferWrapper.TYPE_DOUBLE:
+ positionsD = new DoubleBufferWrapper(buffer) ;
+ if (debug) System.out.println("double positions buffer") ;
+ break ;
+ default:
+ throw new IllegalArgumentException
+ ("\nposition buffer must be FloatBuffer or DoubleBuffer") ;
+ }
+
+ if (vertexColors) {
+ buffer = ga.getColorRefBuffer() ;
+ initialColorIndex = ga.getInitialColorIndex() ;
+
+ switch (BufferWrapper.getBufferType(buffer)) {
+ case BufferWrapper.TYPE_BYTE:
+ colorsB = new ByteBufferWrapper(buffer) ;
+ if (debug) System.out.println("byte colors buffer") ;
+ break ;
+ case BufferWrapper.TYPE_FLOAT:
+ colorsF = new FloatBufferWrapper(buffer) ;
+ if (debug) System.out.println("float colors buffer") ;
+ break ;
+ default:
+ throw new IllegalArgumentException
+ ("\ncolor buffer must be ByteBuffer or FloatBuffer") ;
+ }
+ }
+
+ if (vertexNormals) {
+ buffer = ga.getNormalRefBuffer() ;
+ initialNormalIndex = ga.getInitialNormalIndex() ;
+
+ switch (BufferWrapper.getBufferType(buffer)) {
+ case BufferWrapper.TYPE_FLOAT:
+ normals = new FloatBufferWrapper(buffer) ;
+ if (debug) System.out.println("float normals buffer") ;
+ break ;
+ default:
+ throw new IllegalArgumentException
+ ("\nnormal buffer must be FloatBuffer") ;
+ }
+ }
+ }
+
+ void copyVertex(int pi, int ni, int ci, VertexCopy vc) {
+ pi *= 3 ;
+ if (positionsF != null) {
+ vc.p = new Point3f(positionsF.get(pi + 0),
+ positionsF.get(pi + 1),
+ positionsF.get(pi + 2)) ;
+ }
+ else {
+ vc.p = new Point3f((float)positionsD.get(pi + 0),
+ (float)positionsD.get(pi + 1),
+ (float)positionsD.get(pi + 2)) ;
+ }
+
+ ni *= 3 ;
+ if (vertexNormals) {
+ vc.n = new Vector3f(normals.get(ni + 0),
+ normals.get(ni + 1),
+ normals.get(ni + 2)) ;
+ }
+
+ if (vertexColor3) {
+ ci *= 3 ;
+ if (colorsB != null) {
+ vc.c3 = new Color3f
+ ((colorsB.get(ci + 0) & 0xff) * ByteToFloatScale,
+ (colorsB.get(ci + 1) & 0xff) * ByteToFloatScale,
+ (colorsB.get(ci + 2) & 0xff) * ByteToFloatScale) ;
+ }
+ else {
+ vc.c3 = new Color3f(colorsF.get(ci + 0),
+ colorsF.get(ci + 1),
+ colorsF.get(ci + 2)) ;
+ }
+ vc.c = vc.c3 ;
+ }
+ else if (vertexColor4) {
+ ci *= 4 ;
+ if (colorsB != null) {
+ vc.c4 = new Color4f
+ ((colorsB.get(ci + 0) & 0xff) * ByteToFloatScale,
+ (colorsB.get(ci + 1) & 0xff) * ByteToFloatScale,
+ (colorsB.get(ci + 2) & 0xff) * ByteToFloatScale,
+ (colorsB.get(ci + 3) & 0xff) * ByteToFloatScale) ;
+ }
+ else {
+ vc.c4 = new Color4f(colorsF.get(ci + 0),
+ colorsF.get(ci + 1),
+ colorsF.get(ci + 2),
+ colorsF.get(ci + 3)) ;
+ }
+ vc.c = vc.c4 ;
+ }
+ }
+
+ public void processVertex(int v, int stripFlag) {
+ copyVertex(v + initialPositionIndex,
+ v + initialNormalIndex,
+ v + initialColorIndex, vc) ;
+
+ processVertexCopy(vc, stripFlag) ;
+ }
+ }
+
+ /**
+ * This class implements the GeometryAccessor interface for
+ * non-interleaved indexed geometry arrays accessed with NIO.
+ */
+ private class IndexedByRefGeometryNIO extends ByRefGeometryNIO {
+ IndexArrays ia = new IndexArrays() ;
+ VertexIndices vi = new VertexIndices() ;
+
+ IndexedByRefGeometryNIO(GeometryArray ga) {
+ super(ga) ;
+ getIndexArrays(ga, ia) ;
+ }
+
+ public void processVertex(int v, int stripFlag) {
+ getVertexIndices(v, ia, vi) ;
+ copyVertex(vi.pi, vi.ni, vi.ci, vc) ;
+ processIndexedVertexCopy(vc, vi, stripFlag) ;
+ }
+ }
+
+ /**
+ * Convert a GeometryArray to compression stream elements and add them to
+ * this stream.
+ *
+ * @param ga GeometryArray to convert
+ * @exception IllegalArgumentException if GeometryArray has a
+ * dimensionality or vertex format inconsistent with the CompressionStream
+ */
+ void addGeometryArray(GeometryArray ga) {
+ int firstVertex = 0 ;
+ int validVertexCount = 0 ;
+ int vertexFormat = ga.getVertexFormat() ;
+ GeometryAccessor geometryAccessor = null ;
+
+ if (streamType != getStreamType(ga))
+ throw new IllegalArgumentException
+ ("GeometryArray has inconsistent dimensionality") ;
+
+ if (vertexComponents != getVertexComponents(vertexFormat))
+ throw new IllegalArgumentException
+ ("GeometryArray has inconsistent vertex components") ;
+
+ // Set up for vertex data access semantics.
+ boolean NIO = (vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0 ;
+ boolean byRef = (vertexFormat & GeometryArray.BY_REFERENCE) != 0 ;
+ boolean interleaved = (vertexFormat & GeometryArray.INTERLEAVED) != 0 ;
+ boolean indexedGeometry = ga instanceof IndexedGeometryArray ;
+
+ if (indexedGeometry) {
+ if (debug) System.out.println("indexed") ;
+ // Index arrays will be copied such that valid indices start at
+ // offset 0 in the copied arrays.
+ firstVertex = 0 ;
+ validVertexCount = ((IndexedGeometryArray)ga).getValidIndexCount() ;
+ }
+
+ if (!byRef) {
+ if (debug) System.out.println("by-copy") ;
+ if (indexedGeometry) {
+ geometryAccessor = new IndexedByCopyGeometry(ga) ;
+ }
+ else {
+ firstVertex = 0 ;
+ validVertexCount = ga.getValidVertexCount() ;
+ geometryAccessor = new ByCopyGeometry(ga) ;
+ }
+ }
+ else if (interleaved && NIO) {
+ if (debug) System.out.println("interleaved NIO") ;
+ if (indexedGeometry) {
+ geometryAccessor = new IndexedInterleavedGeometryNIO(ga) ;
+ }
+ else {
+ firstVertex = ga.getInitialVertexIndex() ;
+ validVertexCount = ga.getValidVertexCount() ;
+ geometryAccessor = new InterleavedGeometryNIO(ga) ;
+ }
+ }
+ else if (interleaved && !NIO) {
+ if (debug) System.out.println("interleaved") ;
+ if (indexedGeometry) {
+ geometryAccessor = new IndexedInterleavedGeometryFloat(ga) ;
+ }
+ else {
+ firstVertex = ga.getInitialVertexIndex() ;
+ validVertexCount = ga.getValidVertexCount() ;
+ geometryAccessor = new InterleavedGeometryFloat(ga) ;
+ }
+ }
+ else if (!interleaved && NIO) {
+ if (debug) System.out.println("non-interleaved NIO") ;
+ if (indexedGeometry) {
+ geometryAccessor = new IndexedByRefGeometryNIO(ga) ;
+ }
+ else {
+ firstVertex = 0 ;
+ validVertexCount = ga.getValidVertexCount() ;
+ geometryAccessor = new ByRefGeometryNIO(ga) ;
+ }
+ }
+ else if (!interleaved && !NIO) {
+ if (debug) System.out.println("non-interleaved by-ref") ;
+ if (indexedGeometry) {
+ geometryAccessor = new IndexedByRefGeometry(ga) ;
+ }
+ else {
+ firstVertex = 0 ;
+ validVertexCount = ga.getValidVertexCount() ;
+ geometryAccessor = new ByRefGeometry(ga) ;
+ }
+ }
+
+ // Set up for topology.
+ int stripCount = 0 ;
+ int stripCounts[] = null ;
+ int constantStripLength = 0 ;
+ int replaceCode = RESTART ;
+ boolean strips = false ;
+ boolean implicitStrips = false ;
+
+ if (ga instanceof TriangleStripArray ||
+ ga instanceof IndexedTriangleStripArray ||
+ ga instanceof LineStripArray ||
+ ga instanceof IndexedLineStripArray) {
+
+ strips = true ;
+ replaceCode = REPLACE_OLDEST ;
+ if (debug) System.out.println("strips") ;
+ }
+ else if (ga instanceof TriangleFanArray ||
+ ga instanceof IndexedTriangleFanArray) {
+
+ strips = true ;
+ replaceCode = REPLACE_MIDDLE ;
+ if (debug) System.out.println("fans") ;
+ }
+ else if (ga instanceof QuadArray ||
+ ga instanceof IndexedQuadArray) {
+
+ // Handled as fan arrays with 4 vertices per fan.
+ implicitStrips = true ;
+ constantStripLength = 4 ;
+ replaceCode = REPLACE_MIDDLE ;
+ if (debug) System.out.println("quads") ;
+ }
+
+ // Get strip counts.
+ if (strips) {
+ if (indexedGeometry) {
+ IndexedGeometryStripArray igsa ;
+ igsa = (IndexedGeometryStripArray)ga ;
+
+ stripCount = igsa.getNumStrips() ;
+ stripCounts = new int[stripCount] ;
+ igsa.getStripIndexCounts(stripCounts) ;
+
+ } else {
+ GeometryStripArray gsa ;
+ gsa = (GeometryStripArray)ga ;
+
+ stripCount = gsa.getNumStrips() ;
+ stripCounts = new int[stripCount] ;
+ gsa.getStripVertexCounts(stripCounts) ;
+ }
+ }
+
+ // Build the compression stream for this shape's geometry.
+ int v = firstVertex ;
+ if (strips) {
+ for (int i = 0 ; i < stripCount ; i++) {
+ geometryAccessor.processVertex(v++, RESTART) ;
+ for (int j = 1 ; j < stripCounts[i] ; j++) {
+ geometryAccessor.processVertex(v++, replaceCode) ;
+ }
+ }
+ }
+ else if (implicitStrips) {
+ while (v < firstVertex + validVertexCount) {
+ geometryAccessor.processVertex(v++, RESTART) ;
+ for (int j = 1 ; j < constantStripLength ; j++) {
+ geometryAccessor.processVertex(v++, replaceCode) ;
+ }
+ }
+ }
+ else {
+ while (v < firstVertex + validVertexCount) {
+ geometryAccessor.processVertex(v++, RESTART) ;
+ }
+ }
+ }
+
+ /**
+ * Print the stream to standard output.
+ */
+ void print() {
+ System.out.println("\nstream has " + stream.size() + " entries") ;
+ System.out.println("uncompressed size " + byteCount + " bytes") ;
+ System.out.println("upper position bound: " + mcBounds[1].toString()) ;
+ System.out.println("lower position bound: " + mcBounds[0].toString()) ;
+ System.out.println("X, Y, Z centers (" +
+ ((float)center[0]) + " " +
+ ((float)center[1]) + " " +
+ ((float)center[2]) + ")\n" +
+ "scale " + ((float)scale) + "\n") ;
+
+ Iterator i = stream.iterator() ;
+ while (i.hasNext()) {
+ System.out.println(i.next().toString() + "\n") ;
+ }
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////
+ // //
+ // The following constructors and methods are currently the only public //
+ // members of this class. All other members are subject to revision. //
+ // //
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Creates a CompressionStream from an array of Shape3D scene graph
+ * objects. These Shape3D objects may only consist of a GeometryArray
+ * component and an optional Appearance component. The resulting stream
+ * may be used as input to the GeometryCompressor methods.<p>
+ *
+ * Each Shape3D in the array must be of the same dimensionality (point,
+ * line, or surface) and have the same vertex format as the others.
+ * Texture coordinates are ignored.<p>
+ *
+ * If a color is specified in the material attributes for a Shape3D then
+ * that color is added to the CompressionStream as the current global
+ * color. Subsequent colors as well as any colors bundled with vertices
+ * will override it. Only the material diffuse colors are used; all other
+ * appearance attributes are ignored.<p>
+ *
+ * @param positionQuant
+ * number of bits to quantize each position's X, Y,
+ * and Z components, ranging from 1 to 16
+ *
+ * @param colorQuant
+ * number of bits to quantize each color's R, G, B, and
+ * alpha components, ranging from 2 to 16
+ *
+ * @param normalQuant
+ * number of bits for quantizing each normal's U and V components, ranging
+ * from 0 to 6
+ *
+ * @param shapes
+ * an array of Shape3D scene graph objects containing
+ * GeometryArray objects, all with the same vertex format and
+ * dimensionality
+ *
+ * @exception IllegalArgumentException if any Shape3D has an inconsistent
+ * dimensionality or vertex format, or if any Shape3D contains a geometry
+ * component that is not a GeometryArray
+ *
+ * @see Shape3D
+ * @see GeometryArray
+ * @see GeometryCompressor
+ */
+ public CompressionStream(int positionQuant, int colorQuant,
+ int normalQuant, Shape3D shapes[]) {
+ this() ;
+ if (debug) System.out.println("CompressionStream(Shape3D[]):") ;
+
+ if (shapes == null)
+ throw new IllegalArgumentException("null Shape3D array") ;
+
+ if (shapes.length == 0)
+ throw new IllegalArgumentException("zero-length Shape3D array") ;
+
+ if (shapes[0] == null)
+ throw new IllegalArgumentException("Shape3D at index 0 is null") ;
+
+ long startTime = 0 ;
+ if (benchmark) startTime = System.currentTimeMillis() ;
+
+ Geometry g = shapes[0].getGeometry() ;
+ if (! (g instanceof GeometryArray))
+ throw new IllegalArgumentException
+ ("Shape3D at index 0 is not a GeometryArray") ;
+
+ GeometryArray ga = (GeometryArray)g ;
+ this.streamType = getStreamType(ga) ;
+ this.vertexComponents = getVertexComponents(ga.getVertexFormat()) ;
+
+ // Add global quantization parameters to the start of the stream.
+ addPositionQuantization(positionQuant) ;
+ addColorQuantization(colorQuant) ;
+ addNormalQuantization(normalQuant) ;
+
+ // Loop through all shapes.
+ for (int s = 0 ; s < shapes.length ; s++) {
+ if (debug) System.out.println("\nShape3D " + s + ":") ;
+
+ g = shapes[s].getGeometry() ;
+ if (! (g instanceof GeometryArray))
+ throw new IllegalArgumentException
+ ("Shape3D at index " + s + " is not a GeometryArray") ;
+
+ // Check for material color and add it to the stream if it exists.
+ Appearance a = shapes[s].getAppearance() ;
+ if (a != null) {
+ Material m = a.getMaterial() ;
+ if (m != null) {
+ m.getDiffuseColor(c3f) ;
+ if (vertexColor4) {
+ c4f.set(c3f.x, c3f.y, c3f.z, 1.0f) ;
+ addColor(c4f) ;
+ } else
+ addColor(c3f) ;
+ }
+ }
+
+ // Add the geometry array to the stream.
+ addGeometryArray((GeometryArray)g) ;
+ }
+
+ if (benchmark) {
+ long t = System.currentTimeMillis() - startTime ;
+ System.out.println
+ ("\nCompressionStream:\n" + shapes.length + " shapes in " +
+ (t / 1000f) + " sec") ;
+ }
+ }
+
+ /**
+ * Creates a CompressionStream from an array of Shape3D scene graph
+ * objects. These Shape3D objects may only consist of a GeometryArray
+ * component and an optional Appearance component. The resulting stream
+ * may be used as input to the GeometryCompressor methods.<p>
+ *
+ * Each Shape3D in the array must be of the same dimensionality (point,
+ * line, or surface) and have the same vertex format as the others.
+ * Texture coordinates are ignored.<p>
+ *
+ * If a color is specified in the material attributes for a Shape3D then
+ * that color is added to the CompressionStream as the current global
+ * color. Subsequent colors as well as any colors bundled with vertices
+ * will override it. Only the material diffuse colors are used; all other
+ * appearance attributes are ignored.<p>
+ *
+ * Defaults of 16, 9, and 6 bits are used as the quantization values for
+ * positions, colors, and normals respectively. These are the maximum
+ * resolution values defined for positions and normals; the default of 9
+ * for color is the equivalent of the 8 bits of RGBA component resolution
+ * commonly available in graphics frame buffers.<p>
+ *
+ * @param shapes
+ * an array of Shape3D scene graph objects containing
+ * GeometryArray objects, all with the same vertex format and
+ * dimensionality.
+ *
+ * @exception IllegalArgumentException if any Shape3D has an inconsistent
+ * dimensionality or vertex format, or if any Shape3D contains a geometry
+ * component that is not a GeometryArray
+ *
+ * @see Shape3D
+ * @see GeometryArray
+ * @see GeometryCompressor
+ */
+ public CompressionStream(Shape3D shapes[]) {
+ this(16, 9, 6, shapes) ;
+ }
+
+ /**
+ * Creates a CompressionStream from an array of GeometryInfo objects. The
+ * resulting stream may be used as input to the GeometryCompressor
+ * methods.<p>
+ *
+ * Each GeometryInfo in the array must be of the same dimensionality
+ * (point, line, or surface) and have the same vertex format as the
+ * others. Texture coordinates are ignored.<p>
+ *
+ * @param positionQuant
+ * number of bits to quantize each position's X, Y,
+ * and Z components, ranging from 1 to 16
+ *
+ * @param colorQuant
+ * number of bits to quantize each color's R, G, B, and
+ * alpha components, ranging from 2 to 16
+ *
+ * @param normalQuant
+ * number of bits for quantizing each normal's U and V components, ranging
+ * from 0 to 6
+ *
+ * @param geometry
+ * an array of GeometryInfo objects, all with the same
+ * vertex format and dimensionality
+ *
+ * @exception IllegalArgumentException if any GeometryInfo object has an
+ * inconsistent dimensionality or vertex format
+ *
+ * @see GeometryInfo
+ * @see GeometryCompressor
+ */
+ public CompressionStream(int positionQuant, int colorQuant,
+ int normalQuant, GeometryInfo geometry[]) {
+ this() ;
+ if (debug) System.out.println("CompressionStream(GeometryInfo[])") ;
+
+ if (geometry == null)
+ throw new IllegalArgumentException("null GeometryInfo array") ;
+
+ if (geometry.length == 0)
+ throw new IllegalArgumentException
+ ("zero-length GeometryInfo array") ;
+
+ if (geometry[0] == null)
+ throw new IllegalArgumentException
+ ("GeometryInfo at index 0 is null") ;
+
+ long startTime = 0 ;
+ if (benchmark) startTime = System.currentTimeMillis() ;
+
+ GeometryArray ga = geometry[0].getGeometryArray() ;
+ this.streamType = getStreamType(ga) ;
+ this.vertexComponents = getVertexComponents(ga.getVertexFormat()) ;
+
+ // Add global quantization parameters to the start of the stream.
+ addPositionQuantization(positionQuant) ;
+ addColorQuantization(colorQuant) ;
+ addNormalQuantization(normalQuant) ;
+
+ // Loop through all GeometryInfo objects and add them to the stream.
+ for (int i = 0 ; i < geometry.length ; i++) {
+ if (debug) System.out.println("\nGeometryInfo " + i + ":") ;
+ addGeometryArray(geometry[i].getGeometryArray()) ;
+ }
+
+ if (benchmark) {
+ long t = System.currentTimeMillis() - startTime ;
+ System.out.println
+ ("\nCompressionStream:\n" + geometry.length +
+ " GeometryInfo objects in " + (t / 1000f) + " sec") ;
+ }
+ }
+
+ /**
+ * Creates a CompressionStream from an array of GeometryInfo objects. The
+ * resulting stream may be used as input to the GeometryCompressor
+ * methods.<p>
+ *
+ * Each GeometryInfo in the array must be of the same dimensionality
+ * (point, line, or surface) and have the same vertex format as the
+ * others. Texture coordinates are ignored.<p>
+ *
+ * Defaults of 16, 9, and 6 bits are used as the quantization values for
+ * positions, colors, and normals respectively. These are the maximum
+ * resolution values defined for positions and normals; the default of 9
+ * for color is the equivalent of the 8 bits of RGBA component resolution
+ * commonly available in graphics frame buffers.<p>
+ *
+ * @param geometry
+ * an array of GeometryInfo objects, all with the same
+ * vertex format and dimensionality
+ *
+ * @exception IllegalArgumentException if any GeometryInfo object has an
+ * inconsistent dimensionality or vertex format
+ *
+ * @see GeometryInfo
+ * @see GeometryCompressor
+ */
+ public CompressionStream(GeometryInfo geometry[]) {
+ this(16, 9, 6, geometry) ;
+ }
+
+ /**
+ * Get the original bounds of the coordinate data, in modeling coordinates.
+ * Coordinate data is positioned and scaled to a normalized cube after
+ * compression.
+ *
+ * @return Point3d array of length 2, where the 1st Point3d is the lower
+ * bounds and the 2nd Point3d is the upper bounds.
+ * @since Java 3D 1.3
+ */
+ public Point3d[] getModelBounds() {
+ Point3d[] bounds = new Point3d[2] ;
+ bounds[0] = new Point3d(mcBounds[0]) ;
+ bounds[1] = new Point3d(mcBounds[1]) ;
+ return bounds ;
+ }
+
+ /**
+ * Get the bounds of the compressed object in normalized coordinates.
+ * These have an maximum bounds by [-1.0 .. +1.0] across each axis.
+ *
+ * @return Point3d array of length 2, where the 1st Point3d is the lower
+ * bounds and the 2nd Point3d is the upper bounds.
+ * @since Java 3D 1.3
+ */
+ public Point3d[] getNormalizedBounds() {
+ Point3d[] bounds = new Point3d[2] ;
+ bounds[0] = new Point3d(ncBounds[0]) ;
+ bounds[1] = new Point3d(ncBounds[1]) ;
+ return bounds ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamColor.java b/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamColor.java
new file mode 100644
index 0000000..88fd202
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamColor.java
@@ -0,0 +1,270 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.compression ;
+import javax.vecmath.* ;
+
+/**
+ * This class represents a color in a compression stream. It maintains both
+ * floating-point and quantized representations. This color may be bundled
+ * with a vertex or exist separately as a global color.
+ */
+class CompressionStreamColor extends CompressionStreamElement {
+ private int R, G, B, A ;
+ private boolean color3 ;
+ private boolean color4 ;
+ private float colorR, colorG, colorB, colorA ;
+
+ int rAbsolute, gAbsolute, bAbsolute, aAbsolute ;
+
+ /**
+ * Create a CompressionStreamColor.
+ *
+ * @param stream CompressionStream associated with this element
+ * @param color3 floating-point representation to be encoded
+ */
+ CompressionStreamColor(CompressionStream stream, Color3f c3) {
+ this.color4 = false ;
+ this.color3 = true ;
+ colorR = c3.x ;
+ colorG = c3.y ;
+ colorB = c3.z ;
+ colorA = 0.0f ;
+ stream.byteCount += 12 ;
+ }
+
+ /**
+ * Create a CompressionStreamColor.
+ *
+ * @param stream CompressionStream associated with this element
+ * @param color4 floating-point representation to be encoded
+ */
+ CompressionStreamColor(CompressionStream stream, Color4f c4) {
+ this.color3 = false ;
+ this.color4 = true ;
+ colorR = c4.x ;
+ colorG = c4.y ;
+ colorB = c4.z ;
+ colorA = c4.w ;
+ stream.byteCount += 16 ;
+ }
+
+ /**
+ * Quantize a floating point color to fixed point integer components of
+ * the specified number of bits. The bit length can range from a maximum
+ * of 16 to a minimum of 2 bits since negative colors are not defined.<p>
+ *
+ * The bit length is the total number of bits in the signed version of the
+ * fixed point representation of the input color, which is assumed to
+ * be normalized into the [0..1) range. With the maximum bit length of
+ * 16, 15 bits of positive colors can be represented; a bit length of 9 is
+ * needed to get the 8 bit positive color size in common use.<p>
+ *
+ * @param stream CompressionStream associated with this element
+ * @param table HuffmanTable for collecting data about the quantized
+ * representation of this element
+ */
+ void quantize(CompressionStream stream, HuffmanTable huffmanTable) {
+ // Clamp quantization.
+ int quant =
+ (stream.colorQuant < 2? 2 :
+ (stream.colorQuant > 16? 16 : stream.colorQuant)) ;
+
+ absolute = false ;
+ if (stream.firstColor || stream.colorQuantChanged) {
+ absolute = true ;
+ stream.lastColor[0] = 0 ;
+ stream.lastColor[1] = 0 ;
+ stream.lastColor[2] = 0 ;
+ stream.lastColor[3] = 0 ;
+ stream.firstColor = false ;
+ stream.colorQuantChanged = false ;
+ }
+
+ // Convert the floating point position to s.15 2's complement.
+ if (color3) {
+ R = (int)(colorR * 32768.0) ;
+ G = (int)(colorG * 32768.0) ;
+ B = (int)(colorB * 32768.0) ;
+ A = 0 ;
+ } else if (color4) {
+ R = (int)(colorR * 32768.0) ;
+ G = (int)(colorG * 32768.0) ;
+ B = (int)(colorB * 32768.0) ;
+ A = (int)(colorA * 32768.0) ;
+ }
+
+ // Clamp color components.
+ R = (R > 32767? 32767: (R < 0? 0: R)) ;
+ G = (G > 32767? 32767: (G < 0? 0: G)) ;
+ B = (B > 32767? 32767: (B < 0? 0: B)) ;
+ A = (A > 32767? 32767: (A < 0? 0: A)) ;
+
+ // Compute quantized values.
+ R &= quantizationMask[quant] ;
+ G &= quantizationMask[quant] ;
+ B &= quantizationMask[quant] ;
+ A &= quantizationMask[quant] ;
+
+ // Copy and retain absolute color for mesh buffer lookup.
+ rAbsolute = R ;
+ gAbsolute = G ;
+ bAbsolute = B ;
+ aAbsolute = A ;
+
+ // Compute deltas.
+ R -= stream.lastColor[0] ;
+ G -= stream.lastColor[1] ;
+ B -= stream.lastColor[2] ;
+ A -= stream.lastColor[3] ;
+
+ // Update last values.
+ stream.lastColor[0] += R ;
+ stream.lastColor[1] += G ;
+ stream.lastColor[2] += B ;
+ stream.lastColor[3] += A ;
+
+ // Compute length and shift common to all components.
+ if (color3)
+ computeLengthShift(R, G, B) ;
+
+ else if (color4)
+ computeLengthShift(R, G, B, A) ;
+
+ // 0-length components are allowed only for normals.
+ if (length == 0)
+ length = 1 ;
+
+ // Add this element to the Huffman table associated with this stream.
+ huffmanTable.addColorEntry(length, shift, absolute) ;
+ }
+
+ /**
+ * Output a setColor command.
+ *
+ * @param table HuffmanTable mapping quantized representations to
+ * compressed encodings
+ * @param output CommandStream for collecting compressed output
+ */
+ void outputCommand(HuffmanTable table, CommandStream output) {
+ outputColor(table, output, CommandStream.SET_COLOR, 8) ;
+ }
+
+ /**
+ * Output a color subcommand.
+ *
+ * @param table HuffmanTable mapping quantized representations to
+ * compressed encodings
+ * @param output CommandStream for collecting compressed output
+ */
+ void outputSubcommand(HuffmanTable table, CommandStream output) {
+
+ outputColor(table, output, 0, 6) ;
+ }
+
+ //
+ // Output the final compressed bits to the output command stream.
+ //
+ private void outputColor(HuffmanTable table, CommandStream output,
+ int header, int headerLength) {
+ HuffmanNode t ;
+
+ // Look up the Huffman token for this compression stream element.
+ t = table.getColorEntry(length, shift, absolute) ;
+
+ // Construct the color subcommand components. The maximum length of a
+ // color subcommand is 70 bits (a tag with a length of 6 followed by 4
+ // components of 16 bits each). The subcommand is therefore
+ // constructed initially using just the first 3 components, with the
+ // 4th component added later after the tag has been shifted into the
+ // subcommand header.
+ int componentLength = t.dataLength - t.shift ;
+ int subcommandLength = t.tagLength + (3 * componentLength) ;
+
+ R = (R >> t.shift) & (int)lengthMask[componentLength] ;
+ G = (G >> t.shift) & (int)lengthMask[componentLength] ;
+ B = (B >> t.shift) & (int)lengthMask[componentLength] ;
+
+ long colorSubcommand =
+ (((long)t.tag) << (3 * componentLength)) |
+ (((long)R) << (2 * componentLength)) |
+ (((long)G) << (1 * componentLength)) |
+ (((long)B) << (0 * componentLength)) ;
+
+ if (subcommandLength < 6) {
+ // The header will have some empty bits. The Huffman tag
+ // computation will prevent this if necessary.
+ header |= (int)(colorSubcommand << (6 - subcommandLength)) ;
+ subcommandLength = 0 ;
+ }
+ else {
+ // Move the 1st 6 bits of the subcommand into the header.
+ header |= (int)(colorSubcommand >>> (subcommandLength - 6)) ;
+ subcommandLength -= 6 ;
+ }
+
+ // Add alpha if present.
+ if (color4) {
+ A = (A >> t.shift) & (int)lengthMask[componentLength] ;
+ colorSubcommand = (colorSubcommand << componentLength) | A ;
+ subcommandLength += componentLength ;
+ }
+
+ // Add the header and body to the output buffer.
+ output.addCommand(header, headerLength,
+ colorSubcommand, subcommandLength) ;
+ }
+
+ public String toString() {
+ String d = absolute? "" : "delta " ;
+ String c = (colorR + " " + colorG + " " + colorB +
+ (color4? (" " + colorA): "")) ;
+
+ return
+ "color: " + c + "\n" +
+ " fixed point " + d + + R + " " + G + " " + B + "\n" +
+ " length " + length + " shift " + shift +
+ (absolute? " absolute" : " relative") ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamElement.java b/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamElement.java
new file mode 100644
index 0000000..6000a99
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamElement.java
@@ -0,0 +1,359 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.compression ;
+
+/**
+ * Instances of this class are used as elements in a CompressionStream.
+ * @see CompressionStream
+ */
+abstract class CompressionStreamElement {
+ /**
+ * Bit length of quantized geometric components.
+ */
+ int length ;
+
+ /**
+ * Number of trailing zeros in quantized geometric components.
+ */
+ int shift ;
+
+ /**
+ * If false, geometric component values are represented as differences
+ * from those of the preceding element in the stream.
+ */
+ boolean absolute ;
+
+ /**
+ * Array with elements that can be used as masks to apply a quantization
+ * to the number of bits indicated by the referencing index [0..16].
+ */
+ static final int quantizationMask[] = {
+ 0xFFFF0000, 0xFFFF8000, 0xFFFFC000, 0xFFFFE000,
+ 0xFFFFF000, 0xFFFFF800, 0xFFFFFC00, 0xFFFFFE00,
+ 0xFFFFFF00, 0xFFFFFF80, 0xFFFFFFC0, 0xFFFFFFE0,
+ 0xFFFFFFF0, 0xFFFFFFF8, 0xFFFFFFFC, 0xFFFFFFFE,
+ 0xFFFFFFFF
+ } ;
+
+ /**
+ * Array with elements that can be used as masks to retain the number of
+ * trailing bits of data indicated by the referencing index [0..64]. Used
+ * to clear the leading sign bits of fixed-point 2's complement numbers
+ * and in building the compressed output stream.
+ */
+ static final long lengthMask[] = {
+ 0x0000000000000000L, 0x0000000000000001L,
+ 0x0000000000000003L, 0x0000000000000007L,
+ 0x000000000000000FL, 0x000000000000001FL,
+ 0x000000000000003FL, 0x000000000000007FL,
+ 0x00000000000000FFL, 0x00000000000001FFL,
+ 0x00000000000003FFL, 0x00000000000007FFL,
+ 0x0000000000000FFFL, 0x0000000000001FFFL,
+ 0x0000000000003FFFL, 0x0000000000007FFFL,
+ 0x000000000000FFFFL, 0x000000000001FFFFL,
+ 0x000000000003FFFFL, 0x000000000007FFFFL,
+ 0x00000000000FFFFFL, 0x00000000001FFFFFL,
+ 0x00000000003FFFFFL, 0x00000000007FFFFFL,
+ 0x0000000000FFFFFFL, 0x0000000001FFFFFFL,
+ 0x0000000003FFFFFFL, 0x0000000007FFFFFFL,
+ 0x000000000FFFFFFFL, 0x000000001FFFFFFFL,
+ 0x000000003FFFFFFFL, 0x000000007FFFFFFFL,
+ 0x00000000FFFFFFFFL, 0x00000001FFFFFFFFL,
+ 0x00000003FFFFFFFFL, 0x00000007FFFFFFFFL,
+ 0x0000000FFFFFFFFFL, 0x0000001FFFFFFFFFL,
+ 0x0000003FFFFFFFFFL, 0x0000007FFFFFFFFFL,
+ 0x000000FFFFFFFFFFL, 0x000001FFFFFFFFFFL,
+ 0x000003FFFFFFFFFFL, 0x000007FFFFFFFFFFL,
+ 0x00000FFFFFFFFFFFL, 0x00001FFFFFFFFFFFL,
+ 0x00003FFFFFFFFFFFL, 0x00007FFFFFFFFFFFL,
+ 0x0000FFFFFFFFFFFFL, 0x0001FFFFFFFFFFFFL,
+ 0x0003FFFFFFFFFFFFL, 0x0007FFFFFFFFFFFFL,
+ 0x000FFFFFFFFFFFFFL, 0x001FFFFFFFFFFFFFL,
+ 0x003FFFFFFFFFFFFFL, 0x007FFFFFFFFFFFFFL,
+ 0x00FFFFFFFFFFFFFFL, 0x01FFFFFFFFFFFFFFL,
+ 0x03FFFFFFFFFFFFFFL, 0x07FFFFFFFFFFFFFFL,
+ 0x0FFFFFFFFFFFFFFFL, 0x1FFFFFFFFFFFFFFFL,
+ 0x3FFFFFFFFFFFFFFFL, 0x7FFFFFFFFFFFFFFFL,
+ 0xFFFFFFFFFFFFFFFFL
+ } ;
+
+
+ /**
+ * Computes the quantized representation of this stream element.
+ *
+ * @param stream CompressionStream associated with this element
+ * @param table HuffmanTable for collecting data about the quantized
+ * representation of this element
+ */
+ abstract void quantize(CompressionStream stream, HuffmanTable table) ;
+
+ /**
+ * Outputs the compressed bits representing this stream element.
+ * Some instances of CompressionStreamElement don't require an
+ * implementation and will inherit the stub provided here.
+ *
+ * @param table HuffmanTable mapping quantized representations to
+ * compressed encodings
+ * @param output CommandStream for collecting compressed output
+ */
+ void outputCommand(HuffmanTable table, CommandStream output) {
+ }
+
+ /**
+ * Finds the minimum bits needed to represent the given 16-bit signed 2's
+ * complement integer. For positive integers, this include the first
+ * 1 starting from the left, plus a 0 sign bit; for negative integers,
+ * this includes the first 0 starting from the left, plus a 1 sign bit.
+ * 0 is a special case returning 0; however, 0-length components are valid
+ * ONLY for normals.
+ *
+ * The decompressor uses the data length to determine how many bits of
+ * sign extension to add to the data coming in from the compressed stream
+ * in order to create a 16-bit signed 2's complement integer. E.g., a data
+ * length of 12 indicates that 16-12=4 bits of sign are to be extended.<p>
+ *
+ * @param number a signed 2's complement integer representable in 16 bits
+ * or less
+ * @return minimum number of bits to represent the number
+ */
+ private static final int getLength(int number) {
+ if (number == 0)
+ return 0 ;
+
+ else if ((number & 0x8000) > 0) {
+ // negative numbers
+ if ((number & 0x4000) == 0) return 16 ;
+ if ((number & 0x2000) == 0) return 15 ;
+ if ((number & 0x1000) == 0) return 14 ;
+ if ((number & 0x0800) == 0) return 13 ;
+ if ((number & 0x0400) == 0) return 12 ;
+ if ((number & 0x0200) == 0) return 11 ;
+ if ((number & 0x0100) == 0) return 10 ;
+ if ((number & 0x0080) == 0) return 9 ;
+ if ((number & 0x0040) == 0) return 8 ;
+ if ((number & 0x0020) == 0) return 7 ;
+ if ((number & 0x0010) == 0) return 6 ;
+ if ((number & 0x0008) == 0) return 5 ;
+ if ((number & 0x0004) == 0) return 4 ;
+ if ((number & 0x0002) == 0) return 3 ;
+ if ((number & 0x0001) == 0) return 2 ;
+
+ return 1 ;
+
+ } else {
+ // positive numbers
+ if ((number & 0x4000) > 0) return 16 ;
+ if ((number & 0x2000) > 0) return 15 ;
+ if ((number & 0x1000) > 0) return 14 ;
+ if ((number & 0x0800) > 0) return 13 ;
+ if ((number & 0x0400) > 0) return 12 ;
+ if ((number & 0x0200) > 0) return 11 ;
+ if ((number & 0x0100) > 0) return 10 ;
+ if ((number & 0x0080) > 0) return 9 ;
+ if ((number & 0x0040) > 0) return 8 ;
+ if ((number & 0x0020) > 0) return 7 ;
+ if ((number & 0x0010) > 0) return 6 ;
+ if ((number & 0x0008) > 0) return 5 ;
+ if ((number & 0x0004) > 0) return 4 ;
+ if ((number & 0x0002) > 0) return 3 ;
+
+ return 2 ;
+ }
+ }
+
+ /**
+ * Finds the rightmost 1 bit in the given 16-bit integer. This value is
+ * used by the decompressor to indicate the number of trailing zeros to be
+ * added to the end of the data coming in from the compressed stream,
+ * accomplished by left shifting the data by the indicated amount.
+ * 0 is a special case returning 0.<p>
+ *
+ * @param number an integer representable in 16 bits or less
+ * @return number of trailing zeros
+ */
+ private static final int getShift(int number) {
+ if (number == 0) return 0 ;
+
+ if ((number & 0x0001) > 0) return 0 ;
+ if ((number & 0x0002) > 0) return 1 ;
+ if ((number & 0x0004) > 0) return 2 ;
+ if ((number & 0x0008) > 0) return 3 ;
+ if ((number & 0x0010) > 0) return 4 ;
+ if ((number & 0x0020) > 0) return 5 ;
+ if ((number & 0x0040) > 0) return 6 ;
+ if ((number & 0x0080) > 0) return 7 ;
+ if ((number & 0x0100) > 0) return 8 ;
+ if ((number & 0x0200) > 0) return 9 ;
+ if ((number & 0x0400) > 0) return 10 ;
+ if ((number & 0x0800) > 0) return 11 ;
+ if ((number & 0x1000) > 0) return 12 ;
+ if ((number & 0x2000) > 0) return 13 ;
+ if ((number & 0x4000) > 0) return 14 ;
+
+ return 15 ;
+ }
+
+ /**
+ * Computes common length and shift of 2 numbers.
+ */
+ final void computeLengthShift(int n0, int n1) {
+ int s0 = n0 & 0x8000 ;
+ int s1 = n1 & 0x8000 ;
+
+ // equal sign optimization
+ if (s0 == s1)
+ if (s0 == 0)
+ this.length = getLength(n0 | n1) ;
+ else
+ this.length = getLength(n0 & n1) ;
+ else
+ this.length = getMaximum(getLength(n0), getLength(n1)) ;
+
+ this.shift = getShift(n0 | n1) ;
+ }
+
+
+ /**
+ * Computes common length and shift of 3 numbers.
+ */
+ final void computeLengthShift(int n0, int n1, int n2) {
+ int s0 = n0 & 0x8000 ;
+ int s1 = n1 & 0x8000 ;
+ int s2 = n2 & 0x8000 ;
+
+ // equal sign optimization
+ if (s0 == s1)
+ if (s1 == s2)
+ if (s2 == 0)
+ this.length = getLength(n0 | n1 | n2) ;
+ else
+ this.length = getLength(n0 & n1 & n2) ;
+ else
+ if (s1 == 0)
+ this.length = getMaximum(getLength(n0 | n1),
+ getLength(n2)) ;
+ else
+ this.length = getMaximum(getLength(n0 & n1),
+ getLength(n2)) ;
+ else
+ if (s1 == s2)
+ if (s2 == 0)
+ this.length = getMaximum(getLength(n1 | n2),
+ getLength(n0)) ;
+ else
+ this.length = getMaximum(getLength(n1 & n2),
+ getLength(n0)) ;
+ else
+ if (s0 == 0)
+ this.length = getMaximum(getLength(n0 | n2),
+ getLength(n1)) ;
+ else
+ this.length = getMaximum(getLength(n0 & n2),
+ getLength(n1)) ;
+
+ this.shift = getShift(n0 | n1 | n2) ;
+ }
+
+
+ /**
+ * Computes common length and shift of 4 numbers.
+ */
+ final void computeLengthShift(int n0, int n1, int n2, int n3) {
+ this.length = getMaximum(getLength(n0), getLength(n1),
+ getLength(n2), getLength(n3)) ;
+
+ this.shift = getShift(n0 | n1 | n2 | n3) ;
+ }
+
+
+ /**
+ * Finds the maximum of two integers.
+ */
+ private static final int getMaximum(int x, int y) {
+ if (x > y)
+ return x ;
+ else
+ return y ;
+ }
+
+ /**
+ * Finds the maximum of three integers.
+ */
+ private static final int getMaximum(int x, int y, int z) {
+ if (x > y)
+ if (x > z)
+ return x ;
+ else
+ return z ;
+ else
+ if (y > z)
+ return y ;
+ else
+ return z ;
+ }
+
+ /**
+ * Finds the maximum of four integers.
+ */
+ private static final int getMaximum(int x, int y, int z, int w) {
+ int n0, n1 ;
+
+ if (x > y)
+ n0 = x ;
+ else
+ n0 = y ;
+
+ if (z > w)
+ n1 = z ;
+ else
+ n1 = w ;
+
+ if (n0 > n1)
+ return n0 ;
+ else
+ return n1 ;
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamNormal.java b/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamNormal.java
new file mode 100644
index 0000000..b70ba4e
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamNormal.java
@@ -0,0 +1,673 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.compression ;
+import javax.vecmath.* ;
+
+/**
+ * This class represents a normal in a compression stream. It maintains both
+ * floating-point and quantized representations. This normal may be bundled
+ * with a vertex or exist separately as a global normal.
+ */
+class CompressionStreamNormal extends CompressionStreamElement {
+ private int u, v ;
+ private int specialOctant, specialSextant ;
+ private float normalX, normalY, normalZ ;
+
+ int octant, sextant ;
+ boolean specialNormal ;
+ int uAbsolute, vAbsolute ;
+
+ /**
+ * Create a CompressionStreamNormal.
+ *
+ * @param stream CompressionStream associated with this element
+ * @param normal floating-point representation to be encoded
+ */
+ CompressionStreamNormal(CompressionStream stream, Vector3f normal) {
+ this.normalX = normal.x ;
+ this.normalY = normal.y ;
+ this.normalZ = normal.z ;
+ stream.byteCount += 12 ;
+ }
+
+ //
+ // Normal Encoding Parameterization
+ //
+ // A floating point normal is quantized to a desired number of bits by
+ // comparing it to candidate entries in a table of every possible normal
+ // at that quantization and finding the closest match. This table of
+ // normals is indexed by the following encoding:
+ //
+ // First, points on a unit radius sphere are parameterized by two angles,
+ // th and psi, using usual spherical coordinates. th is the angle about
+ // the y axis, psi is the inclination to the plane containing the point.
+ // The mapping between rectangular and spherical coordinates is:
+ //
+ // x = cos(th)*cos(psi)
+ // y = sin(psi)
+ // z = sin(th)*cos(psi)
+ //
+ // Points on sphere are folded first by octant, and then by sort order
+ // of xyz into one of six sextants. All the table encoding takes place in
+ // the positive octant, in the region bounded by the half spaces:
+ //
+ // x >= z
+ // z >= y
+ // y >= 0
+ //
+ // This triangular shaped patch runs from 0 to 45 degrees in th, and
+ // from 0 to as much as 0.615479709 (MAX_Y_ANG) in psi. The xyz bounds
+ // of the patch is:
+ //
+ // (1, 0, 0) (1/sqrt(2), 0, 1/sqrt(2)) (1/sqrt(3), 1/sqrt(3), 1/sqrt(3))
+ //
+ // When dicing this space up into discrete points, the choice for y is
+ // linear quantization in psi. This means that if the y range is to be
+ // divided up into n segments, the angle of segment j is:
+ //
+ // psi(j) = MAX_Y_ANG*(j/n)
+ //
+ // The y height of the patch (in arc length) is *not* the same as the xz
+ // dimension. However, the subdivision quantization needs to treat xz and
+ // y equally. To achieve this, the th angles are re-parameterized as
+ // reflected psi angles. That is, the i-th point's th is:
+ //
+ // th(i) = asin(tan(psi(i))) = asin(tan(MAX_Y_ANG*(i/n)))
+ //
+ // To go the other direction, the angle th corresponds to the real index r
+ // (in the same 0-n range as i):
+ //
+ // r(th) = n*atan(sin(th))/MAX_Y_ANG
+ //
+ // Rounded to the nearest integer, this gives the closest integer index i
+ // to the xz angle th. Because the triangle has a straight edge on the
+ // line x=z, it is more intuitive to index the xz angles in reverse
+ // order. Thus the two equations above are replaced by:
+ //
+ // th(i) = asin(tan(psi(i))) = asin(tan(MAX_Y_ANG*((n-i)/n)))
+ //
+ // r(th) = n*(1 - atan(sin(th))/MAX_Y_ANG)
+ //
+ // Each level of quantization subdivides the triangular patch twice as
+ // densely. The case in which only the three vertices of the triangle are
+ // present is the first logical stage of representation, but because of
+ // how the table is encoded the first usable case starts one level of
+ // sub-division later. This three point level has an n of 2 by the above
+ // conventions.
+ //
+ private static final int MAX_UV_BITS = 6 ;
+ private static final int MAX_UV_ENTRIES = 64 ;
+
+ private static final double cgNormals[][][][] =
+ new double[MAX_UV_BITS+1][MAX_UV_ENTRIES+1][MAX_UV_ENTRIES+1][3] ;
+
+ private static final double MAX_Y_ANG = 0.615479709 ;
+ private static final double UNITY_14 = 16384.0 ;
+
+ private static void computeNormals() {
+ int inx, iny, inz, n ;
+ double th, psi, qnx, qny, qnz ;
+
+ for (int quant = 0 ; quant <= MAX_UV_BITS ; quant++) {
+ n = 1 << quant ;
+
+ for (int j = 0 ; j <= n ; j++) {
+ for (int i = 0 ; i <= n ; i++) {
+ if (i+j > n) continue ;
+
+ psi = MAX_Y_ANG*(j/((double) n)) ;
+ th = Math.asin(Math.tan(MAX_Y_ANG*((n-i)/((double) n)))) ;
+
+ qnx = Math.cos(th)*Math.cos(psi) ;
+ qny = Math.sin(psi) ;
+ qnz = Math.sin(th)*Math.cos(psi) ;
+
+ // The normal table uses 16-bit components and must be
+ // able to represent both +1.0 and -1.0, so convert the
+ // floating point normal components to fixed point with 14
+ // fractional bits, a unity bit, and a sign bit (s1.14).
+ // Set them back to get the float equivalent.
+ qnx = qnx*UNITY_14 ; inx = (int)qnx ;
+ qnx = inx ; qnx = qnx/UNITY_14 ;
+
+ qny = qny*UNITY_14 ; iny = (int)qny ;
+ qny = iny ; qny = qny/UNITY_14 ;
+
+ qnz = qnz*UNITY_14 ; inz = (int)qnz ;
+ qnz = inz ; qnz = qnz/UNITY_14 ;
+
+ cgNormals[quant][j][i][0] = qnx ;
+ cgNormals[quant][j][i][1] = qny ;
+ cgNormals[quant][j][i][2] = qnz ;
+ }
+ }
+ }
+ }
+
+ //
+ // An inverse sine table is used for each quantization level to take the Y
+ // component of a normal (which is the sine of the inclination angle) and
+ // obtain the closest quantized Y angle.
+ //
+ // At any level of compression, there are a fixed number of different Y
+ // angles (between 0 and MAX_Y_ANG). The inverse table is built to have
+ // slightly more than twice as many entries as y angles at any particular
+ // level; this ensures that the inverse look-up will get within one angle
+ // of the right one. The size of the table should be as small as
+ // possible, but with its delta sine still smaller than the delta sine
+ // between the last two angles to be encoded.
+ //
+ // Example: the inverse sine table has a maximum angle of 0.615479709. At
+ // the maximum resolution of 6 bits there are 65 discrete angles used,
+ // but twice as many are needed for thresholding between angles, so the
+ // delta angle is 0.615479709/128. The difference then between the last
+ // two angles to be encoded is:
+ // sin(0.615479709*128.0/128.0) - sin(0.615479709*127.0/128.0) = 0.003932730
+ //
+ // Using 8 significent bits below the binary point, fixed point can
+ // represent sines in increments of 0.003906250, just slightly smaller.
+ // However, because the maximum Y angle sine is 0.577350269, only 148
+ // instead of 256 table entries are needed.
+ //
+ private static final short inverseSine[][] = new short[MAX_UV_BITS+1][] ;
+
+ // UNITY_14 * sin(MAX_Y_ANGLE)
+ private static final short MAX_SIN_14BIT = 9459 ;
+
+ private static void computeInverseSineTables() {
+ int intSin, deltaSin, intAngle ;
+ double floatSin, floatAngle ;
+ short sin14[] = new short[MAX_UV_ENTRIES+1] ;
+
+ // Build table of sines in s1.14 fixed point for each of the
+ // discrete angles used at maximum resolution.
+ for (int i = 0 ; i <= MAX_UV_ENTRIES ; i++) {
+ sin14[i] = (short)(UNITY_14*Math.sin(i*MAX_Y_ANG/MAX_UV_ENTRIES)) ;
+ }
+
+ for (int quant = 0 ; quant <= MAX_UV_BITS ; quant++) {
+ switch (quant) {
+ default:
+ case 6:
+ // Delta angle: MAX_Y_ANGLE/128.0
+ // Bits below binary point for fixed point delta sine: 8
+ // Integer delta sine: 64
+ // Inverse sine table size: 148 entries
+ deltaSin = 1 << (14 - 8) ;
+ break ;
+ case 5:
+ // Delta angle: MAX_Y_ANGLE/64.0
+ // Bits below binary point for fixed point delta sine: 7
+ // Integer delta sine: 128
+ // Inverse sine table size: 74 entries
+ deltaSin = 1 << (14 - 7) ;
+ break ;
+ case 4:
+ // Delta angle: MAX_Y_ANGLE/32.0
+ // Bits below binary point for fixed point delta sine: 6
+ // Integer delta sine: 256
+ // Inverse sine table size: 37 entries
+ deltaSin = 1 << (14 - 6) ;
+ break ;
+ case 3:
+ // Delta angle: MAX_Y_ANGLE/16.0
+ // Bits below binary point for fixed point delta sine: 5
+ // Integer delta sine: 512
+ // Inverse sine table size: 19 entries
+ deltaSin = 1 << (14 - 5) ;
+ break ;
+ case 2:
+ // Delta angle: MAX_Y_ANGLE/8.0
+ // Bits below binary point for fixed point delta sine: 4
+ // Integer delta sine: 1024
+ // Inverse sine table size: 10 entries
+ deltaSin = 1 << (14 - 4) ;
+ break ;
+ case 1:
+ // Delta angle: MAX_Y_ANGLE/4.0
+ // Bits below binary point for fixed point delta sine: 3
+ // Integer delta sine: 2048
+ // Inverse sine table size: 5 entries
+ deltaSin = 1 << (14 - 3) ;
+ break ;
+ case 0:
+ // Delta angle: MAX_Y_ANGLE/2.0
+ // Bits below binary point for fixed point delta sine: 2
+ // Integer delta sine: 4096
+ // Inverse sine table size: 3 entries
+ deltaSin = 1 << (14 - 2) ;
+ break ;
+ }
+
+ inverseSine[quant] = new short[(MAX_SIN_14BIT/deltaSin) + 1] ;
+
+ intSin = 0 ;
+ for (int i = 0 ; i < inverseSine[quant].length ; i++) {
+ // Compute float representation of integer sine with desired
+ // number of fractional bits by effectively right shifting 14.
+ floatSin = intSin/UNITY_14 ;
+
+ // Compute the angle with this sine value and quantize it.
+ floatAngle = Math.asin(floatSin) ;
+ intAngle = (int)((floatAngle/MAX_Y_ANG) * (1 << quant)) ;
+
+ // Choose the closest of the three nearest quantized values
+ // intAngle-1, intAngle, and intAngle+1.
+ if (intAngle > 0) {
+ if (Math.abs(sin14[intAngle << (6-quant)] - intSin) >
+ Math.abs(sin14[(intAngle-1) << (6-quant)] - intSin))
+ intAngle = intAngle-1 ;
+ }
+
+ if (intAngle < (1 << quant)) {
+ if (Math.abs(sin14[intAngle << (6-quant)] - intSin) >
+ Math.abs(sin14[(intAngle+1) << (6-quant)] - intSin))
+ intAngle = intAngle+1 ;
+ }
+
+ inverseSine[quant][i] = (short)intAngle ;
+ intSin += deltaSin ;
+ }
+ }
+ }
+
+ /**
+ * Compute static tables needed for normal quantization.
+ */
+ static {
+ computeNormals() ;
+ computeInverseSineTables() ;
+ }
+
+ /**
+ * Quantize the floating point normal to a 6-bit octant/sextant plus u,v
+ * components of [0..6] bits. Full resolution is 18 bits and the minimum
+ * is 6 bits.
+ *
+ * @param stream CompressionStream associated with this element
+ * @param table HuffmanTable for collecting data about the quantized
+ * representation of this element
+ */
+ void quantize(CompressionStream stream, HuffmanTable huffmanTable) {
+ double nx, ny, nz, t ;
+
+ // Clamp UV quantization.
+ int quant =
+ (stream.normalQuant < 0? 0 :
+ (stream.normalQuant > 6? 6 : stream.normalQuant)) ;
+
+ nx = normalX ;
+ ny = normalY ;
+ nz = normalZ ;
+
+ octant = 0 ;
+ sextant = 0 ;
+ u = 0 ;
+ v = 0 ;
+
+ // Normalize the fixed point normal to the positive signed octant.
+ if (nx < 0.0) {
+ octant |= 4 ;
+ nx = -nx ;
+ }
+ if (ny < 0.0) {
+ octant |= 2 ;
+ ny = -ny ;
+ }
+ if (nz < 0.0) {
+ octant |= 1 ;
+ nz = -nz ;
+ }
+
+ // Normalize the fixed point normal to the proper sextant of the octant.
+ if (nx < ny) {
+ sextant |= 1 ;
+ t = nx ;
+ nx = ny ;
+ ny = t ;
+ }
+ if (nz < ny) {
+ sextant |= 2 ;
+ t = ny ;
+ ny = nz ;
+ nz = t ;
+ }
+ if (nx < nz) {
+ sextant |= 4 ;
+ t = nx ;
+ nx = nz ;
+ nz = t ;
+ }
+
+ // Convert the floating point y component to s1.14 fixed point.
+ int yInt = (int)(ny * UNITY_14) ;
+
+ // The y component of the normal is the sine of the y angle. Quantize
+ // the y angle by using the fixed point y component as an index into
+ // the inverse sine table of the correct size for the quantization
+ // level. (12 - quant) bits of the s1.14 y normal component are
+ // rolled off with a right shift; the remaining bits then match the
+ // number of bits used to represent the delta sine of the table.
+ int yIndex = inverseSine[quant][yInt >> (12-quant)] ;
+
+ // Search the two xz rows near y for the best match.
+ int ii = 0 ;
+ int jj = 0 ;
+ int n = 1 << quant ;
+ double dot, bestDot = -1 ;
+
+ for (int j = yIndex-1 ; j < yIndex+1 && j <= n ; j++) {
+ if (j < 0)
+ continue ;
+
+ for (int i = 0 ; i <= n ; i++) {
+ if (i+j > n)
+ continue ;
+
+ dot = nx * cgNormals[quant][j][i][0] +
+ ny * cgNormals[quant][j][i][1] +
+ nz * cgNormals[quant][j][i][2] ;
+
+ if (dot > bestDot) {
+ bestDot = dot ;
+ ii = i ;
+ jj = j ;
+ }
+ }
+ }
+
+ // Convert u and v to standard grid form.
+ u = ii << (6 - quant) ;
+ v = jj << (6 - quant) ;
+
+ // Check for special normals and specially encode them.
+ specialNormal = false ;
+ if (u == 64 && v == 0) {
+ // six coordinate axes case
+ if (sextant == 0 || sextant == 2) {
+ // +/- x-axis
+ specialSextant = 0x6 ;
+ specialOctant = ((octant & 4) != 0)? 0x2 : 0 ;
+
+ } else if (sextant == 3 || sextant == 1) {
+ // +/- y-axis
+ specialSextant = 0x6 ;
+ specialOctant = 4 | (((octant & 2) != 0)? 0x2 : 0) ;
+
+ } else if (sextant == 5 || sextant == 4) {
+ // +/- z-axis
+ specialSextant = 0x7 ;
+ specialOctant = ((octant & 1) != 0)? 0x2 : 0 ;
+ }
+ specialNormal = true ;
+ u = v = 0 ;
+
+ } else if (u == 0 && v == 64) {
+ // eight mid point case
+ specialSextant = 6 | (octant >> 2) ;
+ specialOctant = ((octant & 0x3) << 1) | 1 ;
+ specialNormal = true ;
+ u = v = 0 ;
+ }
+
+ // Compute deltas if possible.
+ // Use the non-normalized ii and jj indices.
+ int du = 0 ;
+ int dv = 0 ;
+ int uv64 = 64 >> (6 - quant) ;
+
+ absolute = false ;
+ if (stream.firstNormal || stream.normalQuantChanged ||
+ stream.lastSpecialNormal || specialNormal) {
+ // The first normal by definition is absolute, and normals cannot
+ // be represented as deltas to or from special normals, nor from
+ // normals with a different quantization.
+ absolute = true ;
+ stream.firstNormal = false ;
+ stream.normalQuantChanged = false ;
+
+ } else if (stream.lastOctant == octant &&
+ stream.lastSextant == sextant) {
+ // Deltas are always allowed within the same sextant/octant.
+ du = ii - stream.lastU ;
+ dv = jj - stream.lastV ;
+
+ } else if (stream.lastOctant != octant &&
+ stream.lastSextant == sextant &&
+ (((sextant == 1 || sextant == 5) &&
+ (stream.lastOctant & 3) == (octant & 3)) ||
+ ((sextant == 0 || sextant == 4) &&
+ (stream.lastOctant & 5) == (octant & 5)) ||
+ ((sextant == 2 || sextant == 3) &&
+ (stream.lastOctant & 6) == (octant & 6)))) {
+ // If the sextants are the same, the octants can differ only when
+ // they are bordering each other on the same edge that the
+ // sextant has.
+ du = ii - stream.lastU ;
+ dv = -jj - stream.lastV ;
+
+ // Can't delta by less than -64.
+ if (dv < -uv64) absolute = true ;
+
+ // Can't delta doubly defined points.
+ if (jj == 0) absolute = true ;
+
+ } else if (stream.lastOctant == octant &&
+ stream.lastSextant != sextant &&
+ ((sextant == 0 && stream.lastSextant == 4) ||
+ (sextant == 4 && stream.lastSextant == 0) ||
+ (sextant == 1 && stream.lastSextant == 5) ||
+ (sextant == 5 && stream.lastSextant == 1) ||
+ (sextant == 2 && stream.lastSextant == 3) ||
+ (sextant == 3 && stream.lastSextant == 2))) {
+ // If the octants are the same, the sextants must border on
+ // the i side (this case) or the j side (next case).
+ du = -ii - stream.lastU ;
+ dv = jj - stream.lastV ;
+
+ // Can't delta by less than -64.
+ if (du < -uv64) absolute = true ;
+
+ // Can't delta doubly defined points.
+ if (ii == 0) absolute = true ;
+
+ } else if (stream.lastOctant == octant &&
+ stream.lastSextant != sextant &&
+ ((sextant == 0 && stream.lastSextant == 2) ||
+ (sextant == 2 && stream.lastSextant == 0) ||
+ (sextant == 1 && stream.lastSextant == 3) ||
+ (sextant == 3 && stream.lastSextant == 1) ||
+ (sextant == 4 && stream.lastSextant == 5) ||
+ (sextant == 5 && stream.lastSextant == 4))) {
+ // If the octants are the same, the sextants must border on
+ // the j side (this case) or the i side (previous case).
+ if (((ii + jj ) != uv64) && (ii != 0) && (jj != 0)) {
+ du = uv64 - ii - stream.lastU ;
+ dv = uv64 - jj - stream.lastV ;
+
+ // Can't delta by greater than +63.
+ if ((du >= uv64) || (dv >= uv64))
+ absolute = true ;
+ } else
+ // Can't delta doubly defined points.
+ absolute = true ;
+
+ } else
+ // Can't delta this normal.
+ absolute = true ;
+
+ if (absolute == false) {
+ // Convert du and dv to standard grid form.
+ u = du << (6 - quant) ;
+ v = dv << (6 - quant) ;
+ }
+
+ // Compute length and shift common to all components.
+ computeLengthShift(u, v) ;
+
+ if (absolute && length > 6) {
+ // Absolute normal u, v components are unsigned 6-bit integers, so
+ // truncate the 0 sign bit for values > 0x001f.
+ length = 6 ;
+ }
+
+ // Add this element to the Huffman table associated with this stream.
+ huffmanTable.addNormalEntry(length, shift, absolute) ;
+
+ // Save current normal as last.
+ stream.lastSextant = sextant ;
+ stream.lastOctant = octant ;
+ stream.lastU = ii ;
+ stream.lastV = jj ;
+ stream.lastSpecialNormal = specialNormal ;
+
+ // Copy and retain absolute normal for mesh buffer lookup.
+ uAbsolute = ii ;
+ vAbsolute = jj ;
+ }
+
+ /**
+ * Output a setNormal command.
+ *
+ * @param table HuffmanTable mapping quantized representations to
+ * compressed encodings
+ * @param output CommandStream for collecting compressed output
+ */
+ void outputCommand(HuffmanTable table, CommandStream output) {
+ outputNormal(table, output, CommandStream.SET_NORM, 8) ;
+ }
+
+ /**
+ * Output a normal subcommand.
+ *
+ * @param table HuffmanTable mapping quantized representations to
+ * compressed encodings
+ * @param output CommandStream for collecting compressed output
+ */
+ void outputSubcommand(HuffmanTable table, CommandStream output) {
+ outputNormal(table, output, 0, 6) ;
+ }
+
+ //
+ // Output the final compressed bits to the output command stream.
+ //
+ private void outputNormal(HuffmanTable table, CommandStream output,
+ int header, int headerLength) {
+
+ HuffmanNode t ;
+
+ // Look up the Huffman token for this compression stream element.
+ t = table.getNormalEntry(length, shift, absolute) ;
+
+ // Construct the normal subcommand.
+ int componentLength = t.dataLength - t.shift ;
+ int subcommandLength = 0 ;
+ long normalSubcommand = 0 ;
+
+ if (absolute) {
+ // A 3-bit sextant and a 3-bit octant are always present.
+ subcommandLength = t.tagLength + 6 ;
+
+ if (specialNormal)
+ // Use the specially-encoded sextant and octant.
+ normalSubcommand =
+ (t.tag << 6) | (specialSextant << 3) | specialOctant ;
+ else
+ // Use the general encoding rule.
+ normalSubcommand =
+ (t.tag << 6) | (sextant << 3) | octant ;
+ } else {
+ // The tag is immediately followed by the u and v delta components.
+ subcommandLength = t.tagLength ;
+ normalSubcommand = t.tag ;
+ }
+
+ // Add the u and v values to the subcommand.
+ subcommandLength += (2 * componentLength) ;
+
+ u = (u >> t.shift) & (int)lengthMask[componentLength] ;
+ v = (v >> t.shift) & (int)lengthMask[componentLength] ;
+
+ normalSubcommand =
+ (normalSubcommand << (2 * componentLength)) |
+ (u << (1 * componentLength)) |
+ (v << (0 * componentLength)) ;
+
+ if (subcommandLength < 6) {
+ // The header will have some empty bits. The Huffman tag
+ // computation will prevent this if necessary.
+ header |= (int)(normalSubcommand << (6 - subcommandLength)) ;
+ subcommandLength = 0 ;
+ }
+ else {
+ // Move the 1st 6 bits of the subcommand into the header.
+ header |= (int)(normalSubcommand >>> (subcommandLength - 6)) ;
+ subcommandLength -= 6 ;
+ }
+
+ // Add the header and body to the output buffer.
+ output.addCommand(header, headerLength,
+ normalSubcommand, subcommandLength) ;
+ }
+
+ public String toString() {
+ String fixed ;
+
+ if (specialNormal)
+ fixed = " special normal, sextant " + specialSextant +
+ " octant " + specialOctant ;
+
+ else if (absolute)
+ fixed = " sextant " + sextant + " octant " + octant +
+ " u " + u + " v " + v ;
+ else
+ fixed = " du " + u + " dv " + v ;
+
+ return
+ "normal: " + normalX + " " + normalY + " " + normalZ + "\n"
+ + fixed + "\n" + " length " + length + " shift " + shift +
+ (absolute? " absolute" : " relative") ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamVertex.java b/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamVertex.java
new file mode 100644
index 0000000..ad49b28
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/compression/CompressionStreamVertex.java
@@ -0,0 +1,318 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.compression ;
+import javax.vecmath.* ;
+
+/**
+ * This class represents a vertex in a compression stream. It maintains both
+ * floating-point and quantized representations of the vertex position along
+ * with meshing and vertex replacement flags for line and surface
+ * primitives. If normals or colors are bundled with geometry vertices then
+ * instances of this class will also contain references to normal or color
+ * stream elements.
+ */
+class CompressionStreamVertex extends CompressionStreamElement {
+ private int X, Y, Z ;
+ private int meshFlag ;
+ private int stripFlag ;
+ private float floatX, floatY, floatZ ;
+
+ int xAbsolute, yAbsolute, zAbsolute ;
+ CompressionStreamColor color = null ;
+ CompressionStreamNormal normal = null ;
+
+ /**
+ * Create a CompressionStreamVertex with the given parameters.
+ *
+ * @param stream CompressionStream associated with this vertex
+ * @param p position
+ * @param n normal bundled with this vertex or null if not bundled
+ * @param c color bundled with this vertex or null if not bundled
+ * @param stripFlag CompressionStream.RESTART,
+ * CompressionStream.REPLACE_OLDEST, or CompressionStream.REPLACE_MIDDLE
+ * @param meshFlag CompressionStream.MESH_PUSH or
+ * CompressionStream.NO_MESH_PUSH
+ */
+ CompressionStreamVertex(CompressionStream stream,
+ Point3f p, Vector3f n, Color3f c,
+ int stripFlag, int meshFlag) {
+
+ this(stream, p, n, stripFlag, meshFlag) ;
+
+ if (stream.vertexColor3)
+ color = new CompressionStreamColor(stream, c) ;
+ }
+
+ /**
+ * Create a CompressionStreamVertex with the given parameters.
+ *
+ * @param stream CompressionStream associated with this vertex
+ * @param p position
+ * @param n normal bundled with this vertex or null if not bundled
+ * @param c color bundled with this vertex or null if not bundled
+ * @param stripFlag CompressionStream.RESTART,
+ * CompressionStream.REPLACE_OLDEST, or CompressionStream.REPLACE_MIDDLE
+ * @param meshFlag CompressionStream.MESH_PUSH or
+ * CompressionStream.NO_MESH_PUSH
+ */
+ CompressionStreamVertex(CompressionStream stream,
+ Point3f p, Vector3f n, Color4f c,
+ int stripFlag, int meshFlag) {
+
+ this(stream, p, n, stripFlag, meshFlag) ;
+
+ if (stream.vertexColor4)
+ color = new CompressionStreamColor(stream, c) ;
+ }
+
+ /**
+ * Create a CompressionStreamVertex with the given parameters.
+ *
+ * @param stream CompressionStream associated with this vertex
+ * @param p position
+ * @param n normal bundled with this vertex or null if not bundled
+ * @param stripFlag CompressionStream.RESTART,
+ * CompressionStream.REPLACE_OLDEST, or CompressionStream.REPLACE_MIDDLE
+ * @param meshFlag CompressionStream.MESH_PUSH or
+ * CompressionStream.NO_MESH_PUSH
+ */
+ CompressionStreamVertex(CompressionStream stream, Point3f p, Vector3f n,
+ int stripFlag, int meshFlag) {
+
+ this.stripFlag = stripFlag ;
+ this.meshFlag = meshFlag ;
+ this.floatX = p.x ;
+ this.floatY = p.y ;
+ this.floatZ = p.z ;
+
+ stream.byteCount += 12 ;
+ stream.vertexCount++ ;
+
+ if (p.x < stream.mcBounds[0].x) stream.mcBounds[0].x = p.x ;
+ if (p.y < stream.mcBounds[0].y) stream.mcBounds[0].y = p.y ;
+ if (p.z < stream.mcBounds[0].z) stream.mcBounds[0].z = p.z ;
+
+ if (p.x > stream.mcBounds[1].x) stream.mcBounds[1].x = p.x ;
+ if (p.y > stream.mcBounds[1].y) stream.mcBounds[1].y = p.y ;
+ if (p.z > stream.mcBounds[1].z) stream.mcBounds[1].z = p.z ;
+
+ if (stream.vertexNormals)
+ normal = new CompressionStreamNormal(stream, n) ;
+ }
+
+ /**
+ * Quantize the floating point position to fixed point integer components
+ * of the specified number of bits. The bit length can range from 1 to 16.
+ *
+ * @param stream CompressionStream associated with this element
+ * @param table HuffmanTable for collecting data about the quantized
+ * representation of this element
+ */
+ void quantize(CompressionStream stream, HuffmanTable huffmanTable) {
+ double px, py, pz ;
+
+ // Clamp quantization.
+ int quant =
+ (stream.positionQuant < 1? 1 :
+ (stream.positionQuant > 16? 16 : stream.positionQuant)) ;
+
+ absolute = false ;
+ if (stream.firstPosition || stream.positionQuantChanged) {
+ absolute = true ;
+ stream.lastPosition[0] = 0 ;
+ stream.lastPosition[1] = 0 ;
+ stream.lastPosition[2] = 0 ;
+ stream.firstPosition = false ;
+ stream.positionQuantChanged = false ;
+ }
+
+ // Normalize position to the unit cube. This is bounded by the open
+ // intervals (-1..1) on each axis.
+ px = (floatX - stream.center[0]) * stream.scale ;
+ py = (floatY - stream.center[1]) * stream.scale ;
+ pz = (floatZ - stream.center[2]) * stream.scale ;
+
+ // Convert the floating point position to s.15 2's complement.
+ // ~1.0 -> 32767 (0x00007fff) [ ~1.0 = 32767.0/32768.0]
+ // ~-1.0 -> -32767 (0xffff8001) [~-1.0 = -32767.0/32768.0]
+ X = (int)(px * 32768.0) ;
+ Y = (int)(py * 32768.0) ;
+ Z = (int)(pz * 32768.0) ;
+
+ // Compute quantized values.
+ X &= quantizationMask[quant] ;
+ Y &= quantizationMask[quant] ;
+ Z &= quantizationMask[quant] ;
+
+ // Update quantized bounds.
+ if (X < stream.qcBounds[0].x) stream.qcBounds[0].x = X ;
+ if (Y < stream.qcBounds[0].y) stream.qcBounds[0].y = Y ;
+ if (Z < stream.qcBounds[0].z) stream.qcBounds[0].z = Z ;
+
+ if (X > stream.qcBounds[1].x) stream.qcBounds[1].x = X ;
+ if (Y > stream.qcBounds[1].y) stream.qcBounds[1].y = Y ;
+ if (Z > stream.qcBounds[1].z) stream.qcBounds[1].z = Z ;
+
+ // Copy and retain absolute position for mesh buffer lookup.
+ xAbsolute = X ;
+ yAbsolute = Y ;
+ zAbsolute = Z ;
+
+ // Compute deltas.
+ X -= stream.lastPosition[0] ;
+ Y -= stream.lastPosition[1] ;
+ Z -= stream.lastPosition[2] ;
+
+ // Update last values.
+ stream.lastPosition[0] += X ;
+ stream.lastPosition[1] += Y ;
+ stream.lastPosition[2] += Z ;
+
+ // Deltas which exceed the range of 16-bit signed 2's complement
+ // numbers are handled by sign-extension of the 16th bit in order to
+ // effect a 16-bit wrap-around.
+ X = (X << 16) >> 16 ;
+ Y = (Y << 16) >> 16 ;
+ Z = (Z << 16) >> 16 ;
+
+ // Compute length and shift common to all components.
+ computeLengthShift(X, Y, Z) ;
+
+ // 0-length components are allowed only for normals.
+ if (length == 0)
+ length = 1 ;
+
+ // Add this element to the Huffman table associated with this stream.
+ huffmanTable.addPositionEntry(length, shift, absolute) ;
+
+ // Quantize any bundled color or normal.
+ if (color != null)
+ color.quantize(stream, huffmanTable) ;
+
+ if (normal != null)
+ normal.quantize(stream, huffmanTable) ;
+
+ // Push this vertex into the mesh buffer mirror, if necessary, so it
+ // can be retrieved for computing deltas when mesh buffer references
+ // are subsequently encountered during the quantization pass.
+ if (meshFlag == stream.MESH_PUSH)
+ stream.meshBuffer.push(this) ;
+ }
+
+ /**
+ * Output the final compressed bits to the compression command stream.
+ *
+ * @param table HuffmanTable mapping quantized representations to
+ * compressed encodings
+ * @param output CommandStream for collecting compressed output
+ */
+ void outputCommand(HuffmanTable huffmanTable, CommandStream outputBuffer) {
+
+ HuffmanNode t ;
+ int command = CommandStream.VERTEX ;
+
+ // Look up the Huffman token for this compression stream element. The
+ // values of length and shift found there will override the
+ // corresponding fields in this element, which represent best-case
+ // compression without regard to tag length.
+ t = huffmanTable.getPositionEntry(length, shift, absolute) ;
+
+ // Construct the position subcommand.
+ int componentLength = t.dataLength - t.shift ;
+ int subcommandLength = t.tagLength + (3 * componentLength) ;
+
+ X = (X >> t.shift) & (int)lengthMask[componentLength] ;
+ Y = (Y >> t.shift) & (int)lengthMask[componentLength] ;
+ Z = (Z >> t.shift) & (int)lengthMask[componentLength] ;
+
+ long positionSubcommand =
+ (((long)t.tag) << (3 * componentLength)) |
+ (((long)X) << (2 * componentLength)) |
+ (((long)Y) << (1 * componentLength)) |
+ (((long)Z) << (0 * componentLength)) ;
+
+ if (subcommandLength < 6) {
+ // The header will have some empty bits. The Huffman tag
+ // computation will prevent this if necessary.
+ command |= (int)(positionSubcommand << (6 - subcommandLength)) ;
+ subcommandLength = 0 ;
+ }
+ else {
+ // Move the 1st 6 bits of the subcommand into the header.
+ command |= (int)(positionSubcommand >>> (subcommandLength - 6)) ;
+ subcommandLength -= 6 ;
+ }
+
+ // Construct the vertex command body.
+ long body =
+ (((long)stripFlag) << (subcommandLength + 1)) |
+ (((long)meshFlag) << (subcommandLength + 0)) |
+ (positionSubcommand & lengthMask[subcommandLength]) ;
+
+ // Add the vertex command to the output buffer.
+ outputBuffer.addCommand(command, 8, body, subcommandLength + 3) ;
+
+ // Output any normal and color subcommands.
+ if (normal != null)
+ normal.outputSubcommand(huffmanTable, outputBuffer) ;
+
+ if (color != null)
+ color.outputSubcommand(huffmanTable, outputBuffer) ;
+ }
+
+ public String toString() {
+ String d = absolute? "" : "delta " ;
+ String c = (color == null? "": "\n\n " + color.toString()) ;
+ String n = (normal == null? "": "\n\n " + normal.toString()) ;
+
+ return
+ "position: " + floatX + " " + floatY + " " + floatZ + "\n" +
+ "fixed point " + d + + X + " " + Y + " " + Z + "\n" +
+ "length " + length + " shift " + shift +
+ (absolute? " absolute" : " relative") + "\n" +
+ "strip flag " + stripFlag + " mesh flag " + meshFlag +
+ c + n ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/compression/GeometryCompressor.java b/src/classes/share/com/sun/j3d/utils/compression/GeometryCompressor.java
new file mode 100644
index 0000000..82b3865
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/compression/GeometryCompressor.java
@@ -0,0 +1,203 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.compression ;
+
+import javax.media.j3d.* ;
+import javax.vecmath.* ;
+import java.util.* ;
+import java.io.* ;
+
+/**
+ * A GeometryCompressor takes a stream of geometric elements and
+ * quantization parameters (the CompressionStream object) and
+ * compresses it into a stream of commands as defined by appendix B
+ * of the Java 3D specification. The resulting data may be output
+ * in the form of a CompressedGeometry node component or appended
+ * to a CompressedGeometryFile.
+ *
+ * @see CompressionStream
+ * @see CompressedGeometry
+ * @see CompressedGeometryFile
+ */
+public class GeometryCompressor {
+ private static final boolean benchmark = false ;
+ private static final boolean printStream = false ;
+ private static final boolean printHuffman = false ;
+
+ private HuffmanTable huffmanTable ;
+ private CommandStream outputBuffer ;
+ private CompressedGeometryHeader cgHeader ;
+ private long startTime ;
+
+ public GeometryCompressor() {
+ // Create a compressed geometry header.
+ cgHeader = new CompressedGeometryHeader() ;
+
+ // v1.0.0 - pre-FCS
+ // v1.0.1 - fixed winding order, FCS version (J3D 1.1.2)
+ // v1.0.2 - normal component length maximum 6, LII hardware (J3D 1.2)
+ cgHeader.majorVersionNumber = 1 ;
+ cgHeader.minorVersionNumber = 0 ;
+ cgHeader.minorMinorVersionNumber = 2 ;
+ }
+
+ /**
+ * Compress a stream into a CompressedGeometry node component.
+ *
+ * @param stream CompressionStream containing the geometry to be compressed
+ * @return a CompressedGeometry node component
+ */
+ public CompressedGeometry compress(CompressionStream stream) {
+ CompressedGeometry cg ;
+
+ compressStream(stream) ;
+ cg = new CompressedGeometry(cgHeader, outputBuffer.getBytes()) ;
+
+ outputBuffer.clear() ;
+ return cg ;
+ }
+
+ /**
+ * Compress a stream and append the output to a CompressedGeometryFile.
+ * The resource remains open for subsequent updates; its close() method
+ * must be called to create a valid compressed geometry resource file.
+ *
+ * @param stream CompressionStream containing the geometry to be compressed
+ * @param f a currently open CompressedGeometryFile with write access
+ * @exception IOException if write fails
+ */
+ public void compress(CompressionStream stream, CompressedGeometryFile f)
+ throws IOException {
+
+ compressStream(stream) ;
+ f.write(cgHeader, outputBuffer.getBytes()) ;
+
+ outputBuffer.clear() ;
+ }
+
+ //
+ // Compress the stream and put the results in the output buffer.
+ // Set up the CompressedGeometryHeader object.
+ //
+ private void compressStream(CompressionStream stream) {
+ if (benchmark) startTime = System.currentTimeMillis() ;
+
+ // Create the Huffman table.
+ huffmanTable = new HuffmanTable() ;
+
+ // Quantize the stream, compute deltas between consecutive elements if
+ // possible, and histogram the data length distribution.
+ stream.quantize(huffmanTable) ;
+
+ // Compute tags for stream tokens.
+ huffmanTable.computeTags() ;
+
+ // Create the output buffer and assemble the compressed output.
+ outputBuffer = new CommandStream(stream.getByteCount() / 3) ;
+ stream.outputCommands(huffmanTable, outputBuffer) ;
+
+ // Print any desired info.
+ if (benchmark) printBench(stream) ;
+ if (printStream) stream.print() ;
+ if (printHuffman) huffmanTable.print() ;
+
+ // Set up the compressed geometry header object.
+ cgHeader.bufferType = stream.streamType ;
+ cgHeader.bufferDataPresent = 0 ;
+ cgHeader.lowerBound = new Point3d(stream.ncBounds[0]) ;
+ cgHeader.upperBound = new Point3d(stream.ncBounds[1]) ;
+
+ if (stream.vertexNormals)
+ cgHeader.bufferDataPresent |=
+ CompressedGeometryHeader.NORMAL_IN_BUFFER ;
+
+ if (stream.vertexColor3 || stream.vertexColor4)
+ cgHeader.bufferDataPresent |=
+ CompressedGeometryHeader.COLOR_IN_BUFFER ;
+
+ if (stream.vertexColor4)
+ cgHeader.bufferDataPresent |=
+ CompressedGeometryHeader.ALPHA_IN_BUFFER ;
+
+ cgHeader.start = 0 ;
+ cgHeader.size = outputBuffer.getByteCount() ;
+
+ // Clear the huffman table for next use.
+ huffmanTable.clear() ;
+ }
+
+ private void printBench(CompressionStream stream) {
+ long t = System.currentTimeMillis() - startTime ;
+ int vertexCount = stream.getVertexCount() ;
+ int meshReferenceCount = stream.getMeshReferenceCount() ;
+ int totalVertices = meshReferenceCount + vertexCount ;
+ float meshPercent = 100f * meshReferenceCount/(float)totalVertices ;
+
+ float compressionRatio =
+ stream.getByteCount() / ((float)outputBuffer.getByteCount()) ;
+
+ int vertexBytes =
+ 12 + (stream.vertexColor3 ? 12 : 0) +
+ (stream.vertexColor4 ? 16 : 0) + (stream.vertexNormals ? 12 : 0) ;
+
+ float compressedVertexBytes =
+ outputBuffer.getByteCount() / (float)totalVertices ;
+
+ System.out.println
+ ("\nGeometryCompressor:\n" + totalVertices + " total vertices\n" +
+ vertexCount + " streamed vertices\n" + meshReferenceCount +
+ " mesh buffer references (" + meshPercent + "%)\n" +
+ stream.getByteCount() + " bytes streamed geometry compressed to " +
+ outputBuffer.getByteCount() + " in " + (t/1000f) + " sec\n" +
+ (stream.getByteCount()/(float)t) + " kbytes/sec, " +
+ "stream compression ratio " + compressionRatio + "\n\n" +
+ vertexBytes + " original bytes per vertex, " +
+ compressedVertexBytes + " compressed bytes per vertex\n" +
+ "total vertex compression ratio " +
+ (vertexBytes / (float)compressedVertexBytes) + "\n\n" +
+ "lower bound " + stream.ncBounds[0].toString() +"\n" +
+ "upper bound " + stream.ncBounds[1].toString()) ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/compression/HuffmanNode.java b/src/classes/share/com/sun/j3d/utils/compression/HuffmanNode.java
new file mode 100644
index 0000000..b75f961
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/compression/HuffmanNode.java
@@ -0,0 +1,223 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.compression ;
+import java.util.* ;
+
+/**
+ * Instances of this class are used as the nodes of binary trees representing
+ * mappings of tags to compression stream elements. Tags are descriptors
+ * inserted into the compression command stream that specify the encoding of
+ * immediately succeeding data elements.<p>
+ *
+ * The tag assignments in such a tree are computed from the paths taken from
+ * the root to the leaf nodes. Each leaf node represents the particular way
+ * one or more compression stream elements wound up being encoded with respect
+ * to various combinations of data lengths, shifts, and absolute/relative
+ * status.<p>
+ *
+ * Huffman's algorithm for constructing binary trees with minimal weighted
+ * path lengths can be used to optimize the bit lengths of the tags with
+ * respect to the frequency of occurrence of their associated data encodings
+ * in the compression stream. The weighted path length is the sum of the
+ * frequencies of all the leaf nodes times their path lengths to the root of
+ * the tree.<p>
+ *
+ * The length of the longest tag determines the size of the table mapping tags
+ * to data representations. The geometry compression specification limits the
+ * size of the table to 64 entries, so tags cannot be longer than 6 bits. The
+ * depth of the tree is reduced through a process of increasing the data
+ * lengths of less frequently occuring nodes so they can be merged with other
+ * more frequent nodes.
+ */
+class HuffmanNode {
+ int tag, tagLength ;
+ int shift, dataLength ;
+ boolean absolute ;
+
+ private int frequency ;
+ private HuffmanNode child0, child1, mergeNode ;
+ private boolean merged, unmergeable, cleared ;
+
+ void clear() {
+ tag = -1 ;
+ tagLength = -1 ;
+
+ shift = -1 ;
+ dataLength = -1 ;
+ absolute = false ;
+
+ child0 = null ;
+ child1 = null ;
+ mergeNode = null ;
+
+ frequency = 0 ;
+ merged = false ;
+ unmergeable = false ;
+ cleared = true ;
+ }
+
+ HuffmanNode() {
+ clear() ;
+ }
+
+ HuffmanNode(int length, int shift, boolean absolute) {
+ this() ;
+ set(length, shift, absolute) ;
+ }
+
+ final void set(int length, int shift, boolean absolute) {
+ this.dataLength = length ;
+ this.shift = shift ;
+ this.absolute = absolute ;
+ this.cleared = false ;
+ }
+
+ final boolean cleared() {
+ return cleared ;
+ }
+
+ final void addCount() {
+ frequency++ ;
+ }
+
+ final boolean hasCount() {
+ return frequency > 0 ;
+ }
+
+ final boolean tokenEquals(HuffmanNode node) {
+ return
+ this.absolute == node.absolute &&
+ this.dataLength == node.dataLength &&
+ this.shift == node.shift ;
+ }
+
+ void addChildren(HuffmanNode child0, HuffmanNode child1) {
+ this.child0 = child0 ;
+ this.child1 = child1 ;
+ this.frequency = child0.frequency + child1.frequency ;
+ }
+
+ void collectLeaves(int tag, int tagLength, Collection collection) {
+ if (child0 == null) {
+ this.tag = tag ;
+ this.tagLength = tagLength ;
+ collection.add(this) ;
+ } else {
+ child0.collectLeaves((tag << 1) | 0, tagLength + 1, collection) ;
+ child1.collectLeaves((tag << 1) | 1, tagLength + 1, collection) ;
+ }
+ }
+
+ boolean mergeInto(HuffmanNode node) {
+ if (this.absolute == node.absolute) {
+ if (this.dataLength > node.dataLength)
+ node.dataLength = this.dataLength ;
+
+ if (this.shift < node.shift)
+ node.shift = this.shift ;
+
+ node.frequency += this.frequency ;
+ this.mergeNode = node ;
+ this.merged = true ;
+ return true ;
+
+ } else
+ return false ;
+ }
+
+ int incrementLength() {
+ if (shift > 0)
+ shift-- ;
+ else
+ dataLength++ ;
+
+ return dataLength - shift ;
+ }
+
+ final boolean merged() {
+ return merged ;
+ }
+
+ final HuffmanNode getMergeNode() {
+ return mergeNode ;
+ }
+
+ void setUnmergeable() {
+ unmergeable = true ;
+ }
+
+ final boolean unmergeable() {
+ return unmergeable ;
+ }
+
+ public String toString() {
+ return
+ "shift " + shift + " data length " + dataLength +
+ (absolute? " absolute " : " relative ") +
+ "\ntag 0x" + Integer.toHexString(tag) + " tag length " + tagLength +
+ "\nfrequency: " + frequency ;
+ }
+
+ /**
+ * Sorts nodes in ascending order by frequency.
+ */
+ static class FrequencyComparator implements Comparator {
+ public final int compare(Object o1, Object o2) {
+ return ((HuffmanNode)o1).frequency - ((HuffmanNode)o2).frequency ;
+ }
+ }
+
+ /**
+ * Sorts nodes in descending order by tag bit length.
+ */
+ static class TagLengthComparator implements Comparator {
+ public final int compare(Object o1, Object o2) {
+ return ((HuffmanNode)o2).tagLength - ((HuffmanNode)o1).tagLength ;
+ }
+ }
+
+ static FrequencyComparator frequencyComparator = new FrequencyComparator() ;
+ static TagLengthComparator tagLengthComparator = new TagLengthComparator() ;
+}
diff --git a/src/classes/share/com/sun/j3d/utils/compression/HuffmanTable.java b/src/classes/share/com/sun/j3d/utils/compression/HuffmanTable.java
new file mode 100644
index 0000000..89c4a0b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/compression/HuffmanTable.java
@@ -0,0 +1,477 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.compression ;
+import java.util.* ;
+
+/**
+ * This class maintains a map from compression stream elements (tokens) onto
+ * HuffmanNode objects. A HuffmanNode contains a tag describing the
+ * associated token's data length, right shift value, and absolute/relative
+ * status.<p>
+ *
+ * The tags are computed using Huffman's algorithm to build a binary tree with
+ * a minimal total weighted path length. The frequency of each token is
+ * used as its node's weight when building the tree. The path length from the
+ * root to the token's node then indicates the bit length that should be used
+ * for that token's tag in order to minimize the total size of the compressed
+ * stream.
+ */
+class HuffmanTable {
+ private static final int MAX_TAG_LENGTH = 6 ;
+
+ private HuffmanNode positions[] ;
+ private HuffmanNode normals[] ;
+ private HuffmanNode colors[] ;
+
+ /**
+ * Create a new HuffmanTable with entries for all possible position,
+ * normal, and color tokens.
+ */
+ HuffmanTable() {
+ //
+ // Position and color components can have data lengths up to 16
+ // bits, with right shifts up to 15 bits. The position and color
+ // lookup tables are therefore 2*17*16=544 entries in length to
+ // account for all possible combinations of data lengths, shifts,
+ // and relative or absolute status.
+ //
+ colors = new HuffmanNode[544] ;
+ positions = new HuffmanNode[544] ;
+
+ //
+ // Delta normals can have uv components up to 7 bits in length with
+ // right shifts up to 6 bits. Absolute normals can have uv components
+ // up to 6 bits in length with right shifts up to 5 bits. The normal
+ // lookup table is therefore 2*8*7=112 entries in length.
+ //
+ normals = new HuffmanNode[112] ;
+ }
+
+ private final int getPositionIndex(int len, int shift, boolean absolute) {
+ return (absolute? 1:0)*272 + len*16 + shift ;
+ }
+
+ private final int getNormalIndex(int length, int shift, boolean absolute) {
+ return (absolute? 1:0)*56 + length*7 + shift ;
+ }
+
+ private final int getColorIndex(int length, int shift, boolean absolute) {
+ return getPositionIndex(length, shift, absolute) ;
+ }
+
+
+ /**
+ * Add a position entry with the given length, shift, and absolute
+ * status.
+ *
+ * @param length number of bits in each X, Y, and Z component
+ * @param shift number of trailing zeros in each component
+ * @param absolute if false, value represented is a delta from the
+ * previous vertex in the compression stream
+ */
+ void addPositionEntry(int length, int shift, boolean absolute) {
+ addEntry(positions, getPositionIndex(length, shift, absolute),
+ length, shift, absolute) ;
+ }
+
+ /**
+ * Get the position entry associated with the specified length, shift, and
+ * absolute status. This will contain a tag indicating the actual
+ * encoding to be used in the compression command stream, not necessarily
+ * the same as the original length and shift with which the the entry was
+ * created.
+ *
+ * @param length number of bits in each X, Y, and Z component
+ * @param shift number of trailing zeros in each component
+ * @param absolute if false, value represented is a delta from the
+ * previous vertex in the compression stream
+ * @return HuffmanNode mapped to the specified parameters
+ */
+ HuffmanNode getPositionEntry(int length, int shift, boolean absolute) {
+ return getEntry(positions, getPositionIndex(length, shift, absolute)) ;
+ }
+
+ /**
+ * Add a color entry with the given length, shift, and absolute
+ * status.
+ *
+ * @param length number of bits in each R, G, B, and A component
+ * @param shift number of trailing zeros in each component
+ * @param absolute if false, value represented is a delta from the
+ * previous color in the compression stream
+ */
+ void addColorEntry(int length, int shift, boolean absolute) {
+ addEntry(colors, getColorIndex(length, shift, absolute),
+ length, shift, absolute) ;
+ }
+
+ /**
+ * Get the color entry associated with the specified length, shift, and
+ * absolute status. This will contain a tag indicating the actual
+ * encoding to be used in the compression command stream, not necessarily
+ * the same as the original length and shift with which the the entry was
+ * created.
+ *
+ * @param length number of bits in each R, G, B, and A component
+ * @param shift number of trailing zeros in each component
+ * @param absolute if false, value represented is a delta from the
+ * previous color in the compression stream
+ * @return HuffmanNode mapped to the specified parameters
+ */
+ HuffmanNode getColorEntry(int length, int shift, boolean absolute) {
+ return getEntry(colors, getColorIndex(length, shift, absolute)) ;
+ }
+
+ /**
+ * Add a normal entry with the given length, shift, and absolute
+ * status.
+ *
+ * @param length number of bits in each U and V component
+ * @param shift number of trailing zeros in each component
+ * @param absolute if false, value represented is a delta from the
+ * previous normal in the compression stream
+ */
+ void addNormalEntry(int length, int shift, boolean absolute) {
+ addEntry(normals, getNormalIndex(length, shift, absolute),
+ length, shift, absolute) ;
+ }
+
+ /**
+ * Get the normal entry associated with the specified length, shift, and
+ * absolute status. This will contain a tag indicating the actual
+ * encoding to be used in the compression command stream, not necessarily
+ * the same as the original length and shift with which the the entry was
+ * created.
+ *
+ * @param length number of bits in each U and V component
+ * @param shift number of trailing zeros in each component
+ * @param absolute if false, value represented is a delta from the
+ * previous normal in the compression stream
+ * @return HuffmanNode mapped to the specified parameters
+ */
+ HuffmanNode getNormalEntry(int length, int shift, boolean absolute) {
+ return getEntry(normals, getNormalIndex(length, shift, absolute)) ;
+ }
+
+
+ private void addEntry(HuffmanNode table[], int index,
+ int length, int shift, boolean absolute) {
+
+ if (table[index] == null)
+ table[index] = new HuffmanNode(length, shift, absolute) ;
+
+ else if (table[index].cleared())
+ table[index].set(length, shift, absolute) ;
+
+ table[index].addCount() ;
+ }
+
+ private HuffmanNode getEntry(HuffmanNode table[], int index) {
+ HuffmanNode t = table[index] ;
+
+ while (t.merged())
+ t = t.getMergeNode() ;
+
+ return t ;
+ }
+
+ private void getEntries(HuffmanNode table[], Collection c) {
+ for (int i = 0 ; i < table.length ; i++)
+ if (table[i] != null && !table[i].cleared() &&
+ table[i].hasCount() && !table[i].merged())
+ c.add(table[i]) ;
+ }
+
+
+ /**
+ * Clear this HuffmanTable instance.
+ */
+ void clear() {
+ for (int i = 0 ; i < positions.length ; i++)
+ if (positions[i] != null)
+ positions[i].clear() ;
+
+ for (int i = 0 ; i < colors.length ; i++)
+ if (colors[i] != null)
+ colors[i].clear() ;
+
+ for (int i = 0 ; i < normals.length ; i++)
+ if (normals[i] != null)
+ normals[i].clear() ;
+ }
+
+ /**
+ * Compute optimized tags for each position, color, and normal entry.
+ */
+ void computeTags() {
+ LinkedList nodeList = new LinkedList() ;
+ getEntries(positions, nodeList) ;
+ computeTags(nodeList, 3) ;
+
+ nodeList.clear() ;
+ getEntries(colors, nodeList) ;
+ computeTags(nodeList, 3) ;
+
+ nodeList.clear() ;
+ getEntries(normals, nodeList) ;
+ computeTags(nodeList, 2) ;
+ }
+
+ //
+ // Compute tags for a list of Huffman tokens.
+ //
+ private void computeTags(LinkedList nodes, int minComponentCount) {
+ HuffmanNode node0, node1, node2 ;
+
+ // Return if there's nothing to do.
+ if (nodes.isEmpty())
+ return ;
+
+ while (true) {
+ // Sort the nodes in ascending order by frequency.
+ Collections.sort(nodes, HuffmanNode.frequencyComparator) ;
+
+ // Apply Huffman's algorithm to construct a binary tree with a
+ // minimum total weighted path length.
+ node0 = (HuffmanNode)nodes.removeFirst() ;
+ while (nodes.size() > 0) {
+ node1 = (HuffmanNode)nodes.removeFirst() ;
+ node2 = new HuffmanNode() ;
+
+ node2.addChildren(node0, node1) ;
+ addNodeInOrder(nodes, node2, HuffmanNode.frequencyComparator) ;
+
+ node0 = (HuffmanNode)nodes.removeFirst() ;
+ }
+
+ // node0 is the root of the resulting binary tree. Traverse it
+ // assigning tags and lengths to the leaf nodes. The leaves are
+ // collected into the now empty node list.
+ node0.collectLeaves(0, 0, nodes) ;
+
+ // Sort the nodes in descending order by tag length.
+ Collections.sort(nodes, HuffmanNode.tagLengthComparator) ;
+
+ // Check for tag length overrun.
+ if (((HuffmanNode)nodes.getFirst()).tagLength > MAX_TAG_LENGTH) {
+ // Tokens need to be merged and the tree rebuilt with the new
+ // combined frequencies.
+ merge(nodes) ;
+
+ } else {
+ // Increase tag length + data length if they're too small.
+ expand(nodes, minComponentCount) ;
+ break ;
+ }
+ }
+ }
+
+ //
+ // Merge a token with a long tag into some other token. The merged token
+ // will be removed from the list along with any duplicate node the merge
+ // created, reducing the size of the list by 1 or 2 elements until only
+ // unmergeable tokens are left.
+ //
+ private void merge(LinkedList nodes) {
+ ListIterator i = nodes.listIterator(0) ;
+ HuffmanNode node0, node1, node2 ;
+ int index = 0 ;
+
+ while (i.hasNext()) {
+ // Get the node with the longest possibly mergeable tag.
+ node0 = (HuffmanNode)i.next() ;
+ if (node0.unmergeable()) continue ;
+
+ // Try to find a node that can be merged with node0. This is any
+ // node that matches its absolute/relative status.
+ i.remove() ;
+ while (i.hasNext()) {
+ node1 = (HuffmanNode)i.next() ;
+ if (node0.mergeInto(node1)) {
+ // Search for a duplicate of the possibly modified node1
+ // and merge into it so that node weights remain valid.
+ // If a duplicate exists it must be further in the list,
+ // otherwise node0 would have merged into it.
+ i.remove() ;
+ while (i.hasNext()) {
+ node2 = (HuffmanNode)i.next() ;
+ if (node1.tokenEquals(node2)) {
+ node1.mergeInto(node2) ;
+ return ;
+ }
+ }
+ // node1 has no duplicate, so return it to the list.
+ i.add(node1) ;
+ return ;
+ }
+ }
+
+ // node0 can't be merged with any other node; it must be the only
+ // relative or absolute node in the list. Mark it as unmergeable
+ // to avoid unnecessary searches on subsequent calls to merge()
+ // and return it to the list.
+ node0.setUnmergeable() ;
+ i.add(node0) ;
+
+ // Restart the iteration.
+ i = nodes.listIterator(0) ;
+ }
+ }
+
+ //
+ // Empty bits within a compression command header are not allowed. If
+ // the tag length plus the total data length is less than 6 bits then
+ // the token's length must be increased.
+ //
+ private void expand(LinkedList nodes, int minComponentCount) {
+ Iterator i = nodes.iterator() ;
+
+ while (i.hasNext()) {
+ HuffmanNode n = (HuffmanNode)i.next() ;
+
+ while (n.tagLength +
+ (minComponentCount * (n.dataLength - n.shift)) < 6) {
+
+ n.incrementLength() ;
+ }
+ }
+ }
+
+ //
+ // Insert a node into the correct place in a sorted list of nodes.
+ //
+ private void addNodeInOrder(LinkedList l, HuffmanNode node, Comparator c) {
+ ListIterator i = l.listIterator(0) ;
+
+ while (i.hasNext()) {
+ HuffmanNode n = (HuffmanNode)i.next() ;
+ if (c.compare(n, node) > 0) {
+ n = (HuffmanNode)i.previous() ;
+ break ;
+ }
+ }
+ i.add(node) ;
+ }
+
+ /**
+ * Create compression stream commands for decompressors to use to set up
+ * their decompression tables.
+ *
+ * @param output CommandStream which receives the compression commands
+ */
+ void outputCommands(CommandStream output) {
+ LinkedList nodeList = new LinkedList() ;
+ getEntries(positions, nodeList) ;
+ outputCommands(nodeList, output, CommandStream.POSITION_TABLE) ;
+
+ nodeList.clear() ;
+ getEntries(colors, nodeList) ;
+ outputCommands(nodeList, output, CommandStream.COLOR_TABLE) ;
+
+ nodeList.clear() ;
+ getEntries(normals, nodeList) ;
+ outputCommands(nodeList, output, CommandStream.NORMAL_TABLE) ;
+ }
+
+ //
+ // Output a setTable command for each unique token.
+ //
+ private void outputCommands(Collection nodes,
+ CommandStream output, int tableId) {
+
+ Iterator i = nodes.iterator() ;
+ while (i.hasNext()) {
+ HuffmanNode n = (HuffmanNode)i.next() ;
+ int addressRange = (1 << n.tagLength) | n.tag ;
+ int dataLength = (n.dataLength == 16? 0 : n.dataLength) ;
+
+ int command =
+ CommandStream.SET_TABLE | (tableId << 1) | (addressRange >> 6) ;
+
+ long body =
+ ((addressRange & 0x3f) << 9) | (dataLength << 5) |
+ (n.absolute? 0x10 : 0) | n.shift ;
+
+ output.addCommand(command, 8, body, 15) ;
+ }
+ }
+
+ /**
+ * Print a collection of HuffmanNode objects to standard out.
+ *
+ * @param header descriptive string
+ * @param nodes Collection of HuffmanNode objects to print
+ */
+ void print(String header, Collection nodes) {
+ System.out.println(header + "\nentries: " + nodes.size() + "\n") ;
+
+ Iterator i = nodes.iterator() ;
+ while(i.hasNext()) {
+ HuffmanNode n = (HuffmanNode)i.next() ;
+ System.out.println(n.toString() + "\n") ;
+ }
+ }
+
+ /**
+ * Print the contents of this instance to standard out.
+ */
+ void print() {
+ LinkedList nodeList = new LinkedList() ;
+
+ getEntries(positions, nodeList) ;
+ Collections.sort(nodeList, HuffmanNode.frequencyComparator) ;
+ print("\nposition tokens and tags", nodeList) ;
+
+ nodeList.clear() ;
+ getEntries(colors, nodeList) ;
+ Collections.sort(nodeList, HuffmanNode.frequencyComparator) ;
+ print("\ncolor tokens and tags", nodeList) ;
+
+ nodeList.clear() ;
+ getEntries(normals, nodeList) ;
+ Collections.sort(nodeList, HuffmanNode.frequencyComparator) ;
+ print("\nnormal tokens and tags", nodeList) ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/compression/MeshBuffer.java b/src/classes/share/com/sun/j3d/utils/compression/MeshBuffer.java
new file mode 100644
index 0000000..722d9f2
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/compression/MeshBuffer.java
@@ -0,0 +1,238 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.compression ;
+import javax.vecmath.* ;
+
+/**
+ * This class mirrors the vertex mesh buffer stack supported by the geometry
+ * compression semantics.
+ */
+class MeshBuffer {
+ //
+ // The fixed-length mesh buffer stack is represented by circular buffers.
+ // Three stack representations are provided: vertices, positions, and
+ // indices.
+ //
+ // The vertex representation stores references to CompressionStreamVertex
+ // objects. The position representation stores references to Point3f,
+ // Vector3f, Color3f, and Color4f objects, while the index representation
+ // stores indices into externally maintained arrays of those objects. All
+ // these representations may be used independently and all provide access
+ // to the stored references via a mesh buffer index.
+ //
+ // In addition, the position and index representations provide lookup
+ // mechanisms to check if positions or indices exist in the mesh buffer
+ // and return their mesh buffer indices if they do. This is used to
+ // implement a limited meshing algorithm which reduces the number of
+ // vertices when non-stripped abutting facets are added to a compression
+ // stream.
+ //
+ static final int NOT_FOUND = -1 ;
+
+ private static final int SIZE = 16 ;
+ private static final int NAN_HASH =
+ new Point3f(Float.NaN, Float.NaN, Float.NaN).hashCode() ;
+
+ private int topIndex = SIZE - 1 ;
+ private int positionIndices[] = new int[SIZE] ;
+ private int normalIndices[] = new int[SIZE] ;
+ private int colorIndices[] = new int[SIZE] ;
+
+ private int topPosition = SIZE - 1 ;
+ private int positionHashCodes[] = new int[SIZE] ;
+ private Point3f positions[] = new Point3f[SIZE] ;
+ private Vector3f normals[] = new Vector3f[SIZE] ;
+ private Color3f colors3[] = new Color3f[SIZE] ;
+ private Color4f colors4[] = new Color4f[SIZE] ;
+
+ private int topVertex = SIZE - 1 ;
+ private CompressionStreamVertex vertices[] =
+ new CompressionStreamVertex[SIZE] ;
+
+ MeshBuffer() {
+ for (int i = 0 ; i < SIZE ; i++) {
+ positionHashCodes[i] = NAN_HASH ;
+
+ positionIndices[i] = NOT_FOUND ;
+ normalIndices[i] = NOT_FOUND ;
+ colorIndices[i] = NOT_FOUND ;
+ }
+ }
+
+ private static int nextTop(int top) {
+ // The stack top references an element in the fixed-length backing
+ // array in which the stack is stored. Stack elements below it have
+ // decreasing indices into the backing array until element 0, at which
+ // point the indices wrap to the end of the backing array and back to
+ // the top.
+ //
+ // A push is accomplished by incrementing the stack top in a circular
+ // buffer and storing the data into the new stack element it
+ // references. The bottom of the stack is the element with the next
+ // higher index from the top in the backing array, and is overwritten
+ // with each new push.
+ return (top + 1) % SIZE ;
+ }
+
+ private static int flipOffset(int top, int offset) {
+ // Flips an offset relative to the beginning of the backing array to
+ // an offset from the top of the stack. Also works in reverse, from
+ // an offset from the top of the stack to an offset from the beginning
+ // of the backing array.
+ if (offset > top) offset -= SIZE ;
+ return top - offset ;
+ }
+
+ //
+ // Mesh buffer vertex stack. This is currently only used for vertex
+ // lookup during the quantization pass in order to compute delta values;
+ // no mesh reference lookup is necessary.
+ //
+ void push(CompressionStreamVertex v) {
+ topVertex = nextTop(topVertex) ;
+ vertices[topVertex] = v ;
+ }
+
+ CompressionStreamVertex getVertex(int meshReference) {
+ return vertices[flipOffset(topVertex, meshReference)] ;
+ }
+
+
+ //
+ // Mesh buffer index stack and index reference lookup support.
+ //
+ void push(int positionIndex, int normalIndex) {
+ topIndex = nextTop(topIndex) ;
+
+ positionIndices[topIndex] = positionIndex ;
+ normalIndices[topIndex] = normalIndex ;
+ }
+
+ void push(int positionIndex, int colorIndex, int normalIndex) {
+ push(positionIndex, normalIndex) ;
+ colorIndices[topIndex] = colorIndex ;
+ }
+
+ int getMeshReference(int positionIndex) {
+ int index ;
+ for (index = 0 ; index < SIZE ; index++)
+ if (positionIndices[index] == positionIndex)
+ break ;
+
+ if (index == SIZE) return NOT_FOUND ;
+ return flipOffset(topIndex, index) ;
+ }
+
+ int getPositionIndex(int meshReference) {
+ return positionIndices[flipOffset(topIndex, meshReference)] ;
+ }
+
+ int getColorIndex(int meshReference) {
+ return colorIndices[flipOffset(topIndex, meshReference)] ;
+ }
+
+ int getNormalIndex(int meshReference) {
+ return normalIndices[flipOffset(topIndex, meshReference)] ;
+ }
+
+
+ //
+ // Mesh buffer position stack and position reference lookup support.
+ //
+ void push(Point3f position, Vector3f normal) {
+ topPosition = nextTop(topPosition) ;
+
+ positionHashCodes[topPosition] = position.hashCode() ;
+ positions[topPosition] = position ;
+ normals[topPosition] = normal ;
+ }
+
+ void push(Point3f position, Color3f color, Vector3f normal) {
+ push(position, normal) ;
+ colors3[topPosition] = color ;
+ }
+
+ void push(Point3f position, Color4f color, Vector3f normal) {
+ push(position, normal) ;
+ colors4[topPosition] = color ;
+ }
+
+ void push(Point3f position, Object color, Vector3f normal) {
+ push(position, normal) ;
+ if (color instanceof Color3f)
+ colors3[topPosition] = (Color3f)color ;
+ else
+ colors4[topPosition] = (Color4f)color ;
+ }
+
+ int getMeshReference(Point3f position) {
+ int index ;
+ int hashCode = position.hashCode() ;
+
+ for (index = 0 ; index < SIZE ; index++)
+ if (positionHashCodes[index] == hashCode)
+ if (positions[index].equals(position))
+ break ;
+
+ if (index == SIZE) return NOT_FOUND ;
+ return flipOffset(topPosition, index) ;
+ }
+
+ Point3f getPosition(int meshReference) {
+ return positions[flipOffset(topPosition, meshReference)] ;
+ }
+
+ Color3f getColor3(int meshReference) {
+ return colors3[flipOffset(topPosition, meshReference)] ;
+ }
+
+ Color4f getColor4(int meshReference) {
+ return colors4[flipOffset(topPosition, meshReference)] ;
+ }
+
+ Vector3f getNormal(int meshReference) {
+ return normals[flipOffset(topPosition, meshReference)] ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/BBox.java b/src/classes/share/com/sun/j3d/utils/geometry/BBox.java
new file mode 100644
index 0000000..ea54783
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/BBox.java
@@ -0,0 +1,128 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+import java.io.*;
+import java.util.*;
+import javax.vecmath.*;
+
+/**
+ * Bounding Box class for Triangulator.
+ */
+class BBox {
+ int imin; /* lexicographically smallest point, determines min-x */
+ int imax; /* lexicographically largest point, determines max-x */
+ double ymin; /* minimum y-coordinate */
+ double ymax; /* maximum y-coordinate */
+
+ /**
+ * This constructor computes the bounding box of a line segment whose end
+ * points i, j are sorted according to x-coordinates.
+ */
+ BBox(Triangulator triRef, int i, int j) {
+ // assert(InPointsList(i));
+ // assert(InPointsList(j));
+
+ imin = Math.min(i, j);
+ imax = Math.max(i, j);
+ ymin = Math.min(triRef.points[imin].y, triRef.points[imax].y);
+ ymax = Math.max(triRef.points[imin].y, triRef.points[imax].y);
+ }
+
+
+ boolean pntInBBox(Triangulator triRef, int i) {
+ return (((imax < i) ? false :
+ ((imin > i) ? false :
+ ((ymax < triRef.points[i].y) ? false :
+ ((ymin > triRef.points[i].y) ? false : true)))));
+ }
+
+
+
+ boolean BBoxOverlap(BBox bb) {
+ return (((imax < (bb).imin) ? false :
+ ((imin > (bb).imax) ? false :
+ ((ymax < (bb).ymin) ? false :
+ ((ymin > (bb).ymax) ? false : true)))));
+ }
+
+ boolean BBoxContained(BBox bb) {
+ return ((imin <= (bb).imin) && (imax >= (bb).imax) &&
+ (ymin <= (bb).ymin) && (ymax >= (bb).ymax));
+ }
+
+
+ boolean BBoxIdenticalLeaf(BBox bb) {
+ return ((imin == (bb).imin) && (imax == (bb).imax));
+ }
+
+
+ void BBoxUnion(BBox bb1, BBox bb3) {
+ (bb3).imin = Math.min(imin, (bb1).imin);
+ (bb3).imax = Math.max(imax, (bb1).imax);
+ (bb3).ymin = Math.min(ymin, (bb1).ymin);
+ (bb3).ymax = Math.max(ymax, (bb1).ymax);
+ }
+
+
+ double BBoxArea(Triangulator triRef) {
+ return (triRef.points[imax].x - triRef.points[imin].x) * (ymax - ymin);
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Basic.java b/src/classes/share/com/sun/j3d/utils/geometry/Basic.java
new file mode 100644
index 0000000..ed23455
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Basic.java
@@ -0,0 +1,168 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+import java.io.*;
+import java.util.*;
+import javax.vecmath.*;
+
+class Basic {
+
+ static final double D_RND_MAX = 2147483647.0;
+
+
+ static double detExp(double u_x, double u_y, double u_z,
+ double v_x, double v_y, double v_z,
+ double w_x, double w_y, double w_z) {
+
+ return ((u_x) * ((v_y) * (w_z) - (v_z) * (w_y)) -
+ (u_y) * ((v_x) * (w_z) - (v_z) * (w_x)) +
+ (u_z) * ((v_x) * (w_y) - (v_y) * (w_x)));
+ }
+
+
+ static double det3D(Tuple3f u, Tuple3f v, Tuple3f w) {
+ return ((u).x * ((v).y * (w).z - (v).z * (w).y) -
+ (u).y * ((v).x * (w).z - (v).z * (w).x) +
+ (u).z * ((v).x * (w).y - (v).y * (w).x));
+ }
+
+
+ static double det2D(Tuple2f u, Tuple2f v, Tuple2f w) {
+ return (((u).x - (v).x) * ((v).y - (w).y) + ((v).y - (u).y) * ((v).x - (w).x));
+ }
+
+
+ static double length2(Tuple3f u) {
+ return (((u).x * (u).x) + ((u).y * (u).y) + ((u).z * (u).z));
+ }
+
+ static double lengthL1(Tuple3f u) {
+ return (Math.abs((u).x) + Math.abs((u).y) + Math.abs((u).z));
+ }
+
+ static double lengthL2(Tuple3f u) {
+ return Math.sqrt(((u).x * (u).x) + ((u).y * (u).y) + ((u).z * (u).z));
+ }
+
+
+ static double dotProduct(Tuple3f u, Tuple3f v) {
+ return (((u).x * (v).x) + ((u).y * (v).y) + ((u).z * (v).z));
+ }
+
+
+ static double dotProduct2D(Tuple2f u, Tuple2f v) {
+ return (((u).x * (v).x) + ((u).y * (v).y));
+ }
+
+
+ static void vectorProduct(Tuple3f p, Tuple3f q, Tuple3f r) {
+ (r).x = (p).y * (q).z - (q).y * (p).z;
+ (r).y = (q).x * (p).z - (p).x * (q).z;
+ (r).z = (p).x * (q).y - (q).x * (p).y;
+ }
+
+
+ static void vectorAdd( Tuple3f p, Tuple3f q, Tuple3f r) {
+ (r).x = (p).x + (q).x;
+ (r).y = (p).y + (q).y;
+ (r).z = (p).z + (q).z;
+ }
+
+ static void vectorSub( Tuple3f p, Tuple3f q, Tuple3f r) {
+ (r).x = (p).x - (q).x;
+ (r).y = (p).y - (q).y;
+ (r).z = (p).z - (q).z;
+ }
+
+
+ static void vectorAdd2D( Tuple2f p, Tuple2f q, Tuple2f r) {
+ (r).x = (p).x + (q).x;
+ (r).y = (p).y + (q).y;
+ }
+
+
+ static void vectorSub2D( Tuple2f p, Tuple2f q, Tuple2f r) {
+ (r).x = (p).x - (q).x;
+ (r).y = (p).y - (q).y;
+ }
+
+ static void invertVector(Tuple3f p) {
+ (p).x = -(p).x;
+ (p).y = -(p).y;
+ (p).z = -(p).z;
+ }
+
+ static void divScalar(double scalar, Tuple3f u) {
+ (u).x /= scalar;
+ (u).y /= scalar;
+ (u).z /= scalar;
+ }
+
+ static void multScalar2D(double scalar, Tuple2f u) {
+ (u).x *= scalar;
+ (u).y *= scalar;
+ }
+
+
+ static int signEps(double x, double eps) {
+ return ((x <= eps) ? ((x < -eps) ? -1 : 0) : 1);
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/BottleNeck.java b/src/classes/share/com/sun/j3d/utils/geometry/BottleNeck.java
new file mode 100644
index 0000000..6b3c56f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/BottleNeck.java
@@ -0,0 +1,167 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+import java.io.*;
+import java.util.*;
+import javax.vecmath.*;
+
+class BottleNeck {
+
+ static boolean checkArea(Triangulator triRef, int ind4, int ind5) {
+ int ind1, ind2;
+ int i0, i1, i2;
+ double area = 0.0, area1 = 0, area2 = 0.0;
+
+ i0 = triRef.fetchData(ind4);
+ ind1 = triRef.fetchNextData(ind4);
+ i1 = triRef.fetchData(ind1);
+
+ while (ind1 != ind5) {
+ ind2 = triRef.fetchNextData(ind1);
+ i2 = triRef.fetchData(ind2);
+ area =Numerics.stableDet2D(triRef, i0, i1, i2);
+ area1 += area;
+ ind1 = ind2;
+ i1 = i2;
+ }
+
+ if (Numerics.le(area1, triRef.ZERO)) return false;
+
+ ind1 = triRef.fetchNextData(ind5);
+ i1 = triRef.fetchData(ind1);
+ while (ind1 != ind4) {
+ ind2 = triRef.fetchNextData(ind1);
+ i2 = triRef.fetchData(ind2);
+ area = Numerics.stableDet2D(triRef, i0, i1, i2);
+ area2 += area;
+ ind1 = ind2;
+ i1 = i2;
+ }
+
+ if (Numerics.le(area2, triRef.ZERO)) return false;
+ else return true;
+ }
+
+
+ // Yet another check needed in order to handle degenerate cases!
+ static boolean checkBottleNeck(Triangulator triRef,
+ int i1, int i2, int i3, int ind4) {
+ int ind5;
+ int i4, i5;
+ boolean flag;
+
+ i4 = i1;
+
+ ind5 = triRef.fetchPrevData(ind4);
+ i5 = triRef.fetchData(ind5);
+ if ((i5 != i2) && (i5 != i3)) {
+ flag = Numerics.pntInTriangle(triRef, i1, i2, i3, i5);
+ if (flag) return true;
+ }
+
+ if (i2 <= i3) {
+ if (i4 <= i5) flag = Numerics.segIntersect(triRef, i2, i3, i4, i5, -1);
+ else flag = Numerics.segIntersect(triRef, i2, i3, i5, i4, -1);
+ }
+ else {
+ if (i4 <= i5) flag = Numerics.segIntersect(triRef, i3, i2, i4, i5, -1);
+ else flag = Numerics.segIntersect(triRef, i3, i2, i5, i4, -1);
+ }
+ if (flag) return true;
+
+ ind5 = triRef.fetchNextData(ind4);
+ i5 = triRef.fetchData(ind5);
+
+ if ((i5 != i2) && (i5 != i3)) {
+ flag = Numerics.pntInTriangle(triRef, i1, i2, i3, i5);
+ if (flag) return true;
+ }
+
+ if (i2 <= i3) {
+ if (i4 <= i5) flag = Numerics.segIntersect(triRef, i2, i3, i4, i5, -1);
+ else flag = Numerics.segIntersect(triRef, i2, i3, i5, i4, -1);
+ }
+ else {
+ if (i4 <= i5) flag = Numerics.segIntersect(triRef, i3, i2, i4, i5, -1);
+ else flag = Numerics.segIntersect(triRef, i3, i2, i5, i4, -1);
+ }
+
+ if (flag) return true;
+
+ ind5 = triRef.fetchNextData(ind4);
+ i5 = triRef.fetchData(ind5);
+ while (ind5 != ind4) {
+ if (i4 == i5) {
+ if (checkArea(triRef, ind4, ind5)) return true;
+ }
+ ind5 = triRef.fetchNextData(ind5);
+ i5 = triRef.fetchData(ind5);
+ }
+
+ return false;
+ }
+}
+
+
+
+
+
+
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Box.java b/src/classes/share/com/sun/j3d/utils/geometry/Box.java
new file mode 100644
index 0000000..a0e40ec
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Box.java
@@ -0,0 +1,469 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry;
+
+import com.sun.j3d.utils.geometry.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+/**
+ * Box is a geometry primitive created with a given length, width, and height.
+ * It is centered at the origin. By default, it lies within the bounding
+ * box, [-1,-1,-1] and [1,1,1].
+ *
+ * When a texture is applied to a box, it is map CCW like on a Cylinder.
+ * A texture is mapped CCW from the back of the
+ * body. The top and bottom faces are mapped such that the texture appears
+ * front facing when the faces are rotated 90 toward the viewer.
+ * <p>
+ * By default all primitives with the same parameters share their
+ * geometry (e.g., you can have 50 shperes in your scene, but the
+ * geometry is stored only once). A change to one primitive will
+ * effect all shared nodes. Another implication of this
+ * implementation is that the capabilities of the geometry are shared,
+ * and once one of the shared nodes is live, the capabilities cannot
+ * be set. Use the GEOMETRY_NOT_SHARED flag if you do not wish to
+ * share geometry among primitives with the same parameters.
+ */
+
+public class Box extends Primitive {
+
+ /**
+ * Used to designate the front side of the box when using
+ * getShape().
+ *
+ * @see Box#getShape
+ */
+ public static final int FRONT = 0;
+
+ /**
+ * Used to designate the back side of the box when using
+ * getShape().
+ *
+ * @see Box#getShape
+ */
+ public static final int BACK = 1;
+
+ /**
+ * Used to designate the right side of the box when using
+ * getShape().
+ *
+ * @see Box#getShape
+ */
+ public static final int RIGHT = 2;
+
+ /**
+ * Used to designate the left side of the box when using
+ * getShape().
+ *
+ * @see Box#getShape
+ */
+ public static final int LEFT = 3;
+
+ /**
+ * Used to designate the top side of the box when using
+ * getShape().
+ *
+ * @see Box#getShape
+ */
+ public static final int TOP = 4;
+
+ /**
+ * Used to designate the bottom side of the box when using
+ * getShape().
+ *
+ * @see Box#getShape
+ */
+ public static final int BOTTOM = 5;
+
+ float xDim, yDim, zDim;
+
+ int numTexUnit = 1;
+
+ /**
+ * Constructs a default box of 1.0 in all dimensions.
+ * Normals are generated by default, texture coordinates are not.
+ */
+
+ public Box()
+ {
+ this(1.0f, 1.0f, 1.0f, GENERATE_NORMALS, null);
+ }
+
+ /**
+ * Constructs a box of a given dimension and appearance.
+ * Normals are generated by default, texture coordinates are not.
+ *
+ * @param xdim X-dimension size.
+ * @param ydim Y-dimension size.
+ * @param zdim Z-dimension size.
+ * @param ap Appearance
+ */
+
+ public Box(float xdim, float ydim, float zdim, Appearance ap)
+ {
+ this(xdim, ydim, zdim, GENERATE_NORMALS, ap);
+ }
+
+ /**
+ * Constructs a box of a given dimension, flags, and appearance.
+ *
+ * @param xdim X-dimension size.
+ * @param ydim Y-dimension size.
+ * @param zdim Z-dimension size.
+ * @param primflags primitive flags.
+ * @param ap Appearance
+ */
+
+ public Box(float xdim, float ydim, float zdim, int primflags,
+ Appearance ap, int numTexUnit) {
+ int i;
+ double sign;
+
+ xDim = xdim;
+ yDim = ydim;
+ zDim = zdim;
+ flags = primflags;
+ numTexUnit = numTexUnit;
+
+ //Depending on whether normal inward bit is set.
+ if ((flags & GENERATE_NORMALS_INWARD) != 0)
+ sign = -1.0;
+ else sign = 1.0;
+
+// TransformGroup objTrans = new TransformGroup();
+// objTrans.setCapability(ALLOW_CHILDREN_READ);
+// this.addChild(objTrans);
+
+ Shape3D shape[] = new Shape3D[6];
+
+ GeomBuffer cache = null;
+
+ for (i = FRONT; i <= BOTTOM; i++){
+
+ cache = getCachedGeometry(Primitive.BOX, xdim, ydim, zdim, i, i,
+ primflags);
+ if (cache != null) {
+// System.out.println("using cached geometry i = " + i);
+ shape[i] = new Shape3D(cache.getComputedGeometry());
+ numVerts += cache.getNumVerts();
+ numTris += cache.getNumTris();
+ }
+ else {
+
+ GeomBuffer gbuf = new GeomBuffer(4, numTexUnit);
+
+ gbuf.begin(GeomBuffer.QUAD_STRIP);
+ for (int j = 0; j < 2; j++){
+ gbuf.normal3d( (double) normals[i].x*sign,
+ (double) normals[i].y*sign,
+ (double) normals[i].z*sign);
+ gbuf.texCoord2d(tcoords[i*8 + j*2], tcoords[i*8 + j*2 + 1]);
+ gbuf.vertex3d( (double) verts[i*12 + j*3]*xdim,
+ (double) verts[i*12+ j*3 + 1]*ydim,
+ (double) verts[i*12+ j*3 + 2]*zdim );
+ }
+ for (int j = 3; j > 1; j--){
+ gbuf.normal3d( (double) normals[i].x*sign,
+ (double) normals[i].y*sign,
+ (double) normals[i].z*sign);
+ gbuf.texCoord2d(tcoords[i*8 + j*2], tcoords[i*8 + j*2 + 1]);
+ gbuf.vertex3d( (double) verts[i*12 + j*3]*xdim,
+ (double) verts[i*12+ j*3 + 1]*ydim,
+ (double) verts[i*12+ j*3 + 2]*zdim );
+ }
+ gbuf.end();
+ shape[i] = new Shape3D(gbuf.getGeom(flags));
+ numVerts = gbuf.getNumVerts();
+ numTris = gbuf.getNumTris();
+
+ if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
+ cacheGeometry(Primitive.BOX, xdim, ydim, zdim, i, i,
+ primflags, gbuf);
+ }
+ }
+
+ if ((flags & ENABLE_APPEARANCE_MODIFY) != 0) {
+ (shape[i]).setCapability(Shape3D.ALLOW_APPEARANCE_READ);
+ (shape[i]).setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
+ }
+
+ if ((flags & ENABLE_GEOMETRY_PICKING) != 0) {
+ (shape[i]).setCapability(Shape3D.ALLOW_GEOMETRY_READ);
+ }
+
+// objTrans.addChild(shape[i]);
+ this.addChild(shape[i]);
+ }
+
+ if (ap == null){
+ setAppearance();
+ }
+ else setAppearance(ap);
+ }
+
+ public Box(float xdim, float ydim, float zdim, int primflags,
+ Appearance ap) {
+ this(xdim, ydim, zdim, primflags, ap, 1);
+ }
+
+ /**
+ * Gets one of the faces (Shape3D) from the box that contains the
+ * geometry and appearance. This allows users to modify the
+ * appearance or geometry of individual parts.
+ * @param partId The part to return.
+ * @return The Shape3D object associated with the partID. If an
+ * invalid partId is passed in, null is returned.
+ */
+
+ public Shape3D getShape(int partId) {
+ if ((partId >= FRONT) && (partId <= BOTTOM))
+// return (Shape3D)(((Group)getChild(0)).getChild(partId));
+ return (Shape3D)getChild(partId);
+ return null;
+ }
+
+ /**
+ * Sets appearance of the box. This will set each face of the
+ * box to the same appearance. To set each face's appearance
+ * separately, use getShape(partId) to get the
+ * individual shape and call shape.setAppearance(ap).
+ */
+
+ public void setAppearance(Appearance ap){
+// ((Shape3D)((Group)getChild(0)).getChild(TOP)).setAppearance(ap);
+// ((Shape3D)((Group)getChild(0)).getChild(LEFT)).setAppearance(ap);
+// ((Shape3D)((Group)getChild(0)).getChild(RIGHT)).setAppearance(ap);
+// ((Shape3D)((Group)getChild(0)).getChild(FRONT)).setAppearance(ap);
+// ((Shape3D)((Group)getChild(0)).getChild(BACK)).setAppearance(ap);
+// ((Shape3D)((Group)getChild(0)).getChild(BOTTOM)).setAppearance(ap);
+ ((Shape3D)getChild(TOP)).setAppearance(ap);
+ ((Shape3D)getChild(LEFT)).setAppearance(ap);
+ ((Shape3D)getChild(RIGHT)).setAppearance(ap);
+ ((Shape3D)getChild(FRONT)).setAppearance(ap);
+ ((Shape3D)getChild(BACK)).setAppearance(ap);
+ ((Shape3D)getChild(BOTTOM)).setAppearance(ap);
+ }
+
+ /**
+ * Gets the appearance of the specified part of the box.
+ *
+ * @param partId identifier for a given subpart of the box
+ *
+ * @return The appearance object associated with the partID. If an
+ * invalid partId is passed in, null is returned.
+ *
+ * @since Java 3D 1.2.1
+ */
+ public Appearance getAppearance(int partId) {
+ if (partId > BOTTOM || partId < FRONT) return null;
+ return getShape(partId).getAppearance();
+ }
+
+
+ private static final float[] verts = {
+ // front face
+ 1.0f, -1.0f, 1.0f,
+ 1.0f, 1.0f, 1.0f,
+-1.0f, 1.0f, 1.0f,
+-1.0f, -1.0f, 1.0f,
+ // back face
+-1.0f, -1.0f, -1.0f,
+-1.0f, 1.0f, -1.0f,
+ 1.0f, 1.0f, -1.0f,
+ 1.0f, -1.0f, -1.0f,
+ // right face
+ 1.0f, -1.0f, -1.0f,
+ 1.0f, 1.0f, -1.0f,
+ 1.0f, 1.0f, 1.0f,
+ 1.0f, -1.0f, 1.0f,
+ // left face
+-1.0f, -1.0f, 1.0f,
+-1.0f, 1.0f, 1.0f,
+-1.0f, 1.0f, -1.0f,
+-1.0f, -1.0f, -1.0f,
+ // top face
+ 1.0f, 1.0f, 1.0f,
+ 1.0f, 1.0f, -1.0f,
+-1.0f, 1.0f, -1.0f,
+-1.0f, 1.0f, 1.0f,
+ // bottom face
+-1.0f, -1.0f, 1.0f,
+-1.0f, -1.0f, -1.0f,
+ 1.0f, -1.0f, -1.0f,
+ 1.0f, -1.0f, 1.0f,
+ };
+
+ private static final double[] tcoords = {
+ // front
+ 1.0, 0.0,
+ 1.0, 1.0,
+ 0.0, 1.0,
+ 0.0, 0.0,
+ // back
+ 1.0, 0.0,
+ 1.0, 1.0,
+ 0.0, 1.0,
+ 0.0, 0.0,
+ //right
+ 1.0, 0.0,
+ 1.0, 1.0,
+ 0.0, 1.0,
+ 0.0, 0.0,
+ // left
+ 1.0, 0.0,
+ 1.0, 1.0,
+ 0.0, 1.0,
+ 0.0, 0.0,
+ // top
+ 1.0, 0.0,
+ 1.0, 1.0,
+ 0.0, 1.0,
+ 0.0, 0.0,
+ // bottom
+ 0.0, 1.0,
+ 0.0, 0.0,
+ 1.0, 0.0,
+ 1.0, 1.0
+ };
+
+
+ private static final Vector3f[] normals = {
+ new Vector3f( 0.0f, 0.0f, 1.0f), // front face
+ new Vector3f( 0.0f, 0.0f, -1.0f), // back face
+ new Vector3f( 1.0f, 0.0f, 0.0f), // right face
+ new Vector3f(-1.0f, 0.0f, 0.0f), // left face
+ new Vector3f( 0.0f, 1.0f, 0.0f), // top face
+ new Vector3f( 0.0f, -1.0f, 0.0f), // bottom face
+ };
+
+
+ /**
+ * Used to create a new instance of the node. This routine is called
+ * by <code>cloneTree</code> to duplicate the current node.
+ * <code>cloneNode</code> should be overridden by any user subclassed
+ * objects. All subclasses must have their <code>cloneNode</code>
+ * method consist of the following lines:
+ * <P><blockquote><pre>
+ * public Node cloneNode(boolean forceDuplicate) {
+ * UserSubClass usc = new UserSubClass();
+ * usc.duplicateNode(this, forceDuplicate);
+ * return usc;
+ * }
+ * </pre></blockquote>
+ * @param forceDuplicate when set to <code>true</code>, causes the
+ * <code>duplicateOnCloneTree</code> flag to be ignored. When
+ * <code>false</code>, the value of each node's
+ * <code>duplicateOnCloneTree</code> variable determines whether
+ * NodeComponent data is duplicated or copied.
+ *
+ * @see Node#cloneTree
+ * @see Node#duplicateNode
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public Node cloneNode(boolean forceDuplicate) {
+ Box b = new Box(xDim, yDim, zDim, flags, getAppearance());
+ b.duplicateNode(this, forceDuplicate);
+ return b;
+ }
+
+ /**
+ * Copies all node information from <code>originalNode</code> into
+ * the current node. This method is called from the
+ * <code>cloneNode</code> method which is, in turn, called by the
+ * <code>cloneTree</code> method.
+ * <P>
+ * For any <i>NodeComponent</i> objects
+ * contained by the object being duplicated, each <i>NodeComponent</i>
+ * object's <code>duplicateOnCloneTree</code> value is used to determine
+ * whether the <i>NodeComponent</i> should be duplicated in the new node
+ * or if just a reference to the current node should be placed in the
+ * new node. This flag can be overridden by setting the
+ * <code>forceDuplicate</code> parameter in the <code>cloneTree</code>
+ * method to <code>true</code>.
+ *
+ * @param originalNode the original node to duplicate.
+ * @param forceDuplicate when set to <code>true</code>, causes the
+ * <code>duplicateOnCloneTree</code> flag to be ignored. When
+ * <code>false</code>, the value of each node's
+ * <code>duplicateOnCloneTree</code> variable determines whether
+ * NodeComponent data is duplicated or copied.
+ *
+ * @see Node#cloneTree
+ * @see Node#cloneNode
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public void duplicateNode(Node originalNode, boolean forceDuplicate) {
+ super.duplicateNode(originalNode, forceDuplicate);
+ }
+
+ /**
+ * Returns the X-dimension size of the Box
+ *
+ * @since Java 3D 1.2.1
+ */
+ public float getXdimension() {
+ return xDim;
+ }
+
+ /**
+ * Returns the Y-dimension size of the Box
+ *
+ * @since Java 3D 1.2.1
+ */
+ public float getYdimension() {
+ return yDim;
+ }
+
+ /**
+ * Returns the Z-dimension size of the Box
+ *
+ * @since Java 3D 1.2.1
+ */
+ public float getZdimension() {
+ return zDim;
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Bridge.java b/src/classes/share/com/sun/j3d/utils/geometry/Bridge.java
new file mode 100644
index 0000000..8dea58c
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Bridge.java
@@ -0,0 +1,398 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+import java.io.*;
+import java.util.*;
+import javax.vecmath.*;
+
+class Bridge {
+
+ static void constructBridges(Triangulator triRef, int loopMin, int loopMax) {
+ int i, j, numDist, numLeftMost;
+
+ int[] i0 = new int[1];
+ int[] ind0 = new int[1];
+ int[] i1 = new int[1];
+ int[] ind1 = new int[1];
+
+ int[] iTmp = new int[1];
+ int[] indTmp = new int[1];
+
+ if(triRef.noHashingEdges != true)
+ System.out.println("Bridge:constructBridges noHashingEdges is false");
+ if(loopMax <= loopMin)
+ System.out.println("Bridge:constructBridges loopMax<=loopMin");
+ if(loopMin < 0)
+ System.out.println("Bridge:constructBridges loopMin<0");
+ if(loopMax > triRef.numLoops)
+ System.out.println("Bridge:constructBridges loopMax>triRef.numLoops");
+
+ numLeftMost = loopMax - loopMin - 1;
+
+ if (numLeftMost > triRef.maxNumLeftMost) {
+ triRef.maxNumLeftMost = numLeftMost;
+ triRef.leftMost = new Left[numLeftMost];
+ }
+
+ // For each contour, find the left-most vertex. (we will use the fact
+ // that the vertices appear in sorted order!)
+ findLeftMostVertex(triRef, triRef.loops[loopMin], ind0, i0);
+ j = 0;
+ for (i = loopMin + 1; i < loopMax; ++i) {
+ findLeftMostVertex(triRef, triRef.loops[i], indTmp, iTmp);
+ triRef.leftMost[j] = new Left();
+ triRef.leftMost[j].ind = indTmp[0];
+ triRef.leftMost[j].index = iTmp[0];
+
+ ++j;
+ }
+
+ // sort the inner contours according to their left-most vertex
+ sortLeft(triRef.leftMost, numLeftMost);
+
+ // construct bridges. every bridge will eminate at the left-most point of
+ // its corresponding inner loop.
+ numDist = triRef.numPoints + 2 * triRef.numLoops;
+ triRef.maxNumDist = numDist;
+ triRef.distances = new Distance[numDist];
+ for (int k = 0; k < triRef.maxNumDist; k++)
+ triRef.distances[k] = new Distance();
+
+
+ for (j = 0; j < numLeftMost; ++j) {
+ if (!findBridge(triRef, ind0[0], i0[0], triRef.leftMost[j].index, ind1, i1)) {
+ // if (verbose)
+ // fprintf(stderr, "\n\n***** yikes! the loops intersect! *****\n");
+ }
+ if (i1[0] == triRef.leftMost[j].index)
+ // the left-most node of the hole coincides with a node of the
+ // boundary
+ simpleBridge(triRef, ind1[0], triRef.leftMost[j].ind);
+ else
+ // two bridge edges need to be inserted
+ insertBridge(triRef, ind1[0], i1[0], triRef.leftMost[j].ind,
+ triRef.leftMost[j].index);
+ }
+
+ }
+
+
+ /**
+ * We try to find a vertex i1 on the loop which contains i such that i1
+ * is close to start, and such that i1, start is a valid diagonal.
+ */
+ static boolean findBridge(Triangulator triRef, int ind, int i, int start,
+ int[] ind1, int[] i1) {
+ int i0, i2, j, numDist = 0;
+ int ind0, ind2;
+ BBox bb;
+ Distance old[] = null;
+ boolean convex, coneOk;
+
+ // sort the points according to their distance from start.
+ ind1[0] = ind;
+ i1[0] = i;
+ if (i1[0] == start) return true;
+ if (numDist >= triRef.maxNumDist) {
+ // System.out.println("(1) Expanding distances array ...");
+ triRef.maxNumDist += triRef.INC_DIST_BK;
+ old = triRef.distances;
+ triRef.distances = new Distance[triRef.maxNumDist];
+ System.arraycopy(old, 0, triRef.distances, 0, old.length);
+ for (int k = old.length; k < triRef.maxNumDist; k++)
+ triRef.distances[k] = new Distance();
+ }
+
+ triRef.distances[numDist].dist = Numerics.baseLength(triRef.points[start],
+ triRef.points[i1[0]]);
+ triRef.distances[numDist].ind = ind1[0];
+ ++numDist;
+
+
+ ind1[0] = triRef.fetchNextData(ind1[0]);
+ i1[0] = triRef.fetchData(ind1[0]);
+ while (ind1[0] != ind) {
+ if (i1[0] == start) return true;
+ if (numDist >= triRef.maxNumDist) {
+ // System.out.println("(2) Expanding distances array ...");
+ triRef.maxNumDist += triRef.INC_DIST_BK;
+ old = triRef.distances;
+ triRef.distances = new Distance[triRef.maxNumDist];
+ System.arraycopy(old, 0, triRef.distances, 0, old.length);
+ for (int k = old.length; k < triRef.maxNumDist; k++)
+ triRef.distances[k] = new Distance();
+ }
+
+ triRef.distances[numDist].dist = Numerics.baseLength(triRef.points[start],
+ triRef.points[i1[0]]);
+ triRef.distances[numDist].ind = ind1[0];
+ ++numDist;
+ ind1[0] = triRef.fetchNextData(ind1[0]);
+ i1[0] = triRef.fetchData(ind1[0]);
+ }
+
+ // qsort(distances, num_dist, sizeof(distance), &d_comp);
+ sortDistance(triRef.distances, numDist);
+
+ // find a valid diagonal. note that no node with index i1 > start can
+ // be feasible!
+ for (j = 0; j < numDist; ++j) {
+ ind1[0] = triRef.distances[j].ind;
+ i1[0] = triRef.fetchData(ind1[0]);
+ if (i1[0] <= start) {
+ ind0 = triRef.fetchPrevData(ind1[0]);
+ i0 = triRef.fetchData(ind0);
+ ind2 = triRef.fetchNextData(ind1[0]);
+ i2 = triRef.fetchData(ind2);
+ convex = triRef.getAngle(ind1[0]) > 0;
+
+ coneOk = Numerics.isInCone(triRef, i0, i1[0], i2, start, convex);
+ if (coneOk) {
+ bb = new BBox(triRef, i1[0], start);
+ if (!NoHash.noHashEdgeIntersectionExists(triRef, bb, -1, -1, ind1[0], -1))
+ return true;
+ }
+ }
+ }
+
+ // the left-most point of the hole does not lie within the outer
+ // boundary! what is the best bridge in this case??? I make a
+ // brute-force decision... perhaps this should be refined during a
+ // revision of the code...
+ for (j = 0; j < numDist; ++j) {
+ ind1[0] = triRef.distances[j].ind;
+ i1[0] = triRef.fetchData(ind1[0]);
+ ind0 = triRef.fetchPrevData(ind1[0]);
+ i0 = triRef.fetchData(ind0);
+ ind2 = triRef.fetchNextData(ind1[0]);
+ i2 = triRef.fetchData(ind2);
+ bb = new BBox(triRef, i1[0], start);
+ if (!NoHash.noHashEdgeIntersectionExists(triRef, bb, -1, -1, ind1[0], -1))
+ return true;
+ }
+
+ // still no diagonal??? yikes! oh well, this polygon is messed up badly!
+ ind1[0] = ind;
+ i1[0] = i;
+
+ return false;
+ }
+
+
+ static void findLeftMostVertex(Triangulator triRef, int ind, int[] leftInd,
+ int[] leftI) {
+ int ind1, i1;
+
+ ind1 = ind;
+ i1 = triRef.fetchData(ind1);
+ leftInd[0] = ind1;
+ leftI[0] = i1;
+ ind1 = triRef.fetchNextData(ind1);
+ i1 = triRef.fetchData(ind1);
+ while (ind1 != ind) {
+ if (i1 < leftI[0]) {
+ leftInd[0] = ind1;
+ leftI[0] = i1;
+ }
+ else if (i1 == leftI[0]) {
+ if (triRef.getAngle(ind1) < 0) {
+ leftInd[0] = ind1;
+ leftI[0] = i1;
+ }
+ }
+ ind1 = triRef.fetchNextData(ind1);
+ i1 = triRef.fetchData(ind1);
+ }
+
+ }
+
+ static void simpleBridge(Triangulator triRef, int ind1, int ind2) {
+ int prev, next;
+ int i1, i2, prv, nxt;
+ int angle;
+
+
+ // change the links
+ triRef.rotateLinks(ind1, ind2);
+
+ // reset the angles
+ i1 = triRef.fetchData(ind1);
+ next = triRef.fetchNextData(ind1);
+ nxt = triRef.fetchData(next);
+ prev = triRef.fetchPrevData(ind1);
+ prv = triRef.fetchData(prev);
+ angle = Numerics.isConvexAngle(triRef, prv, i1, nxt, ind1);
+ triRef.setAngle(ind1, angle);
+
+ i2 = triRef.fetchData(ind2);
+ next = triRef.fetchNextData(ind2);
+ nxt = triRef.fetchData(next);
+ prev = triRef.fetchPrevData(ind2);
+ prv = triRef.fetchData(prev);
+ angle = Numerics.isConvexAngle(triRef, prv, i2, nxt, ind2);
+ triRef.setAngle(ind2, angle);
+
+ }
+
+
+ static void insertBridge(Triangulator triRef, int ind1, int i1,
+ int ind3, int i3) {
+ int ind2, ind4, prev, next;
+ int prv, nxt, angle;
+ int vcntIndex;
+
+ // duplicate nodes in order to form end points of the bridge edges
+ ind2 = triRef.makeNode(i1);
+ triRef.insertAfter(ind1, ind2);
+
+ // Need to get the original data, before setting it.
+
+ vcntIndex = triRef.list[ind1].getCommonIndex();
+
+ triRef.list[ind2].setCommonIndex(vcntIndex);
+
+
+ ind4 = triRef.makeNode(i3);
+ triRef.insertAfter(ind3, ind4);
+
+ vcntIndex = triRef.list[ind3].getCommonIndex();
+ triRef.list[ind4].setCommonIndex(vcntIndex);
+
+ // insert the bridge edges into the boundary loops
+ triRef.splitSplice(ind1, ind2, ind3, ind4);
+
+ // reset the angles
+ next = triRef.fetchNextData(ind1);
+ nxt = triRef.fetchData(next);
+ prev = triRef.fetchPrevData(ind1);
+ prv = triRef.fetchData(prev);
+ angle = Numerics.isConvexAngle(triRef, prv, i1, nxt, ind1);
+ triRef.setAngle(ind1, angle);
+
+ next = triRef.fetchNextData(ind2);
+ nxt = triRef.fetchData(next);
+ prev = triRef.fetchPrevData(ind2);
+ prv = triRef.fetchData(prev);
+ angle = Numerics.isConvexAngle(triRef, prv, i1, nxt, ind2);
+ triRef.setAngle(ind2, angle);
+
+ next = triRef.fetchNextData(ind3);
+ nxt = triRef.fetchData(next);
+ prev = triRef.fetchPrevData(ind3);
+ prv = triRef.fetchData(prev);
+ angle = Numerics.isConvexAngle(triRef, prv, i3, nxt, ind3);
+ triRef.setAngle(ind3, angle);
+
+ next = triRef.fetchNextData(ind4);
+ nxt = triRef.fetchData(next);
+ prev = triRef.fetchPrevData(ind4);
+ prv = triRef.fetchData(prev);
+ angle = Numerics.isConvexAngle(triRef, prv, i3, nxt, ind4);
+ triRef.setAngle(ind4, angle);
+
+ }
+
+
+ static int l_comp(Left a, Left b) {
+ if (a.index < b.index) return -1;
+ else if (a.index > b.index) return 1;
+ else return 0;
+ }
+
+ static int d_comp(Distance a, Distance b) {
+ if (a.dist < b.dist) return -1;
+ else if (a.dist > b.dist) return 1;
+ else return 0;
+ }
+
+
+ static void sortLeft(Left[] lefts, int numPts) {
+ int i,j;
+ Left swap = new Left();
+
+ for (i = 0; i < numPts; i++){
+ for (j = i + 1; j < numPts; j++){
+ if (l_comp(lefts[i], lefts[j]) > 0){
+ swap.copy(lefts[i]);
+ lefts[i].copy(lefts[j]);
+ lefts[j].copy(swap);
+ }
+ }
+ }
+ }
+
+
+ static void sortDistance(Distance[] distances, int numPts) {
+ int i,j;
+ Distance swap = new Distance();
+
+ for (i = 0; i < numPts; i++){
+ for (j = i + 1; j < numPts; j++){
+ if (d_comp(distances[i], distances[j]) > 0){
+ swap.copy(distances[i]);
+ distances[i].copy(distances[j]);
+ distances[j].copy(swap);
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Clean.java b/src/classes/share/com/sun/j3d/utils/geometry/Clean.java
new file mode 100644
index 0000000..98b2e88
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Clean.java
@@ -0,0 +1,193 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+import java.io.*;
+import java.util.*;
+import javax.vecmath.*;
+
+class Clean {
+
+ static void initPUnsorted(Triangulator triRef, int number) {
+ if (number > triRef.maxNumPUnsorted) {
+ triRef.maxNumPUnsorted = number;
+ triRef.pUnsorted = new Point2f[triRef.maxNumPUnsorted];
+ for(int i = 0; i<triRef.maxNumPUnsorted; i++)
+ triRef.pUnsorted[i] = new Point2f();
+ }
+ }
+
+
+ static int cleanPolyhedralFace(Triangulator triRef, int i1, int i2) {
+ int removed;
+ int i, j, numSorted, index;
+ int ind1, ind2;
+
+ initPUnsorted(triRef, triRef.numPoints);
+
+ for (i = 0; i < triRef.numPoints; ++i)
+ triRef.pUnsorted[i].set(triRef.points[i]);
+
+ // sort points according to lexicographical order
+ /*
+ System.out.println("Points : (Unsorted)");
+ for(i=0; i<triRef.numPoints; i++)
+ System.out.println( i + "pt ( " + triRef.points[i].x + ", " +
+ triRef.points[i].y + ")");
+ */
+
+ // qsort(points, num_pnts, sizeof(point), &p_comp);
+
+ sort(triRef.points, triRef.numPoints);
+
+ /*
+ System.out.println("Points : (Sorted)");
+ for(i=0; i<triRef.numPoints; i++)
+ System.out.println( i +"pt ( " + triRef.points[i].x + ", " +
+ triRef.points[i].y + ")");
+ */
+
+ // eliminate duplicate vertices
+ i = 0;
+ for (j = 1; j < triRef.numPoints; ++j) {
+ if (pComp(triRef.points[i], triRef.points[j]) != 0) {
+ ++i;
+ triRef.points[i] = triRef.points[j];
+ }
+ }
+ numSorted = i + 1;
+ removed = triRef.numPoints - numSorted;
+
+ /*
+ System.out.println("Points : (Sorted and eliminated)");
+ for(i=0; i<triRef.numPoints; i++)
+ System.out.println( i + "pt ( " + triRef.points[i].x + ", " +
+ triRef.points[i].y + ")");
+ */
+
+ // renumber the vertices of the polygonal face
+ for (i = i1; i < i2; ++i) {
+ ind1 = triRef.loops[i];
+ ind2 = triRef.fetchNextData(ind1);
+ index = triRef.fetchData(ind2);
+ while (ind2 != ind1) {
+ j = findPInd(triRef.points, numSorted, triRef.pUnsorted[index]);
+ triRef.updateIndex(ind2, j);
+ ind2 = triRef.fetchNextData(ind2);
+ index = triRef.fetchData(ind2);
+ }
+ j = findPInd(triRef.points, numSorted, triRef.pUnsorted[index]);
+ triRef.updateIndex(ind2, j);
+ }
+
+ triRef.numPoints = numSorted;
+
+ return removed;
+ }
+
+
+ static void sort(Point2f points[], int numPts) {
+ int i,j;
+ Point2f swap = new Point2f();
+
+ for (i = 0; i < numPts; i++){
+ for (j = i + 1; j < numPts; j++){
+ if (pComp(points[i], points[j]) > 0){
+ swap.set(points[i]);
+ points[i].set(points[j]);
+ points[j].set(swap);
+ }
+ }
+ }
+ /*
+ for (i = 0; i < numPts; i++) {
+ System.out.println("pt " + points[i]);
+ }
+ */
+ }
+
+ static int findPInd(Point2f sorted[], int numPts, Point2f pnt) {
+ int i;
+
+ for (i = 0; i < numPts; i++){
+ if ((pnt.x == sorted[i].x) &&
+ (pnt.y == sorted[i].y)){
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ static int pComp(Point2f a, Point2f b) {
+ if (a.x < b.x)
+ return -1;
+ else if (a.x > b.x)
+ return 1;
+ else {
+ if (a.y < b.y)
+ return -1;
+ else if (a.y > b.y)
+ return 1;
+ else
+ return 0;
+ }
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/ColorCube.java b/src/classes/share/com/sun/j3d/utils/geometry/ColorCube.java
new file mode 100644
index 0000000..a64977b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/ColorCube.java
@@ -0,0 +1,175 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry;
+
+import javax.media.j3d.*;
+
+/**
+ * Simple color-per-vertex cube with a different color for each face
+ */
+public class ColorCube extends Shape3D {
+ private static final float[] verts = {
+ // front face
+ 1.0f, -1.0f, 1.0f,
+ 1.0f, 1.0f, 1.0f,
+ -1.0f, 1.0f, 1.0f,
+ -1.0f, -1.0f, 1.0f,
+ // back face
+ -1.0f, -1.0f, -1.0f,
+ -1.0f, 1.0f, -1.0f,
+ 1.0f, 1.0f, -1.0f,
+ 1.0f, -1.0f, -1.0f,
+ // right face
+ 1.0f, -1.0f, -1.0f,
+ 1.0f, 1.0f, -1.0f,
+ 1.0f, 1.0f, 1.0f,
+ 1.0f, -1.0f, 1.0f,
+ // left face
+ -1.0f, -1.0f, 1.0f,
+ -1.0f, 1.0f, 1.0f,
+ -1.0f, 1.0f, -1.0f,
+ -1.0f, -1.0f, -1.0f,
+ // top face
+ 1.0f, 1.0f, 1.0f,
+ 1.0f, 1.0f, -1.0f,
+ -1.0f, 1.0f, -1.0f,
+ -1.0f, 1.0f, 1.0f,
+ // bottom face
+ -1.0f, -1.0f, 1.0f,
+ -1.0f, -1.0f, -1.0f,
+ 1.0f, -1.0f, -1.0f,
+ 1.0f, -1.0f, 1.0f,
+ };
+
+ private static final float[] colors = {
+ // front face (red)
+ 1.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f,
+ // back face (green)
+ 0.0f, 1.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f,
+ // right face (blue)
+ 0.0f, 0.0f, 1.0f,
+ 0.0f, 0.0f, 1.0f,
+ 0.0f, 0.0f, 1.0f,
+ 0.0f, 0.0f, 1.0f,
+ // left face (yellow)
+ 1.0f, 1.0f, 0.0f,
+ 1.0f, 1.0f, 0.0f,
+ 1.0f, 1.0f, 0.0f,
+ 1.0f, 1.0f, 0.0f,
+ // top face (magenta)
+ 1.0f, 0.0f, 1.0f,
+ 1.0f, 0.0f, 1.0f,
+ 1.0f, 0.0f, 1.0f,
+ 1.0f, 0.0f, 1.0f,
+ // bottom face (cyan)
+ 0.0f, 1.0f, 1.0f,
+ 0.0f, 1.0f, 1.0f,
+ 0.0f, 1.0f, 1.0f,
+ 0.0f, 1.0f, 1.0f,
+ };
+
+ double scale;
+
+ /**
+ * Constructs a color cube with unit scale. The corners of the
+ * color cube are [-1,-1,-1] and [1,1,1].
+ */
+ public ColorCube() {
+ QuadArray cube = new QuadArray(24, QuadArray.COORDINATES |
+ QuadArray.COLOR_3);
+
+ cube.setCoordinates(0, verts);
+ cube.setColors(0, colors);
+
+ this.setGeometry(cube);
+
+ scale = 1.0;
+ }
+
+
+ /**
+ * Constructs a color cube with the specified scale. The corners of the
+ * color cube are [-scale,-scale,-scale] and [scale,scale,scale].
+ * @param scale the scale of the cube
+ */
+ public ColorCube(double scale) {
+ QuadArray cube = new QuadArray(24, QuadArray.COORDINATES |
+ QuadArray.COLOR_3);
+
+ float scaledVerts[] = new float[verts.length];
+ for (int i = 0; i < verts.length; i++)
+ scaledVerts[i] = verts[i] * (float)scale;
+
+ cube.setCoordinates(0, scaledVerts);
+ cube.setColors(0, colors);
+
+ this.setGeometry(cube);
+
+ this.scale = scale;
+ }
+
+ /**
+ * @deprecated ColorCube now extends shape so it is no longer necessary
+ * to call this method.
+ */
+ public Shape3D getShape() {
+ return this;
+ }
+
+ /**
+ * Returns the scale of the Cube
+ *
+ * @since Java 3D 1.2.1
+ */
+ public double getScale() {
+ return scale;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Cone.java b/src/classes/share/com/sun/j3d/utils/geometry/Cone.java
new file mode 100644
index 0000000..5673383
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Cone.java
@@ -0,0 +1,428 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry;
+
+import com.sun.j3d.utils.geometry.*;
+import java.io.*;
+import java.util.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+/**
+ * Cone is a geometry primitive defined with a radius and a height.
+ * It is a capped cone centered at the origin with its central axis
+ * aligned along the Y-axis. The center of the cone is defined to be
+ * the center of its bounding box (rather than its centroid).
+ * <p>
+ * If the GENERATE_TEXTURE_COORDS flag is set, the texture coordinates
+ * are generated such that the texture gets mapped onto the cone similarly
+ * to how it gets mapped onto a cylinder, the difference being the top
+ * cap is nonexistent.
+ * <p>
+ * By default all primitives with the same parameters share their
+ * geometry (e.g., you can have 50 shperes in your scene, but the
+ * geometry is stored only once). A change to one primitive will
+ * effect all shared nodes. Another implication of this
+ * implementation is that the capabilities of the geometry are shared,
+ * and once one of the shared nodes is live, the capabilities cannot
+ * be set. Use the GEOMETRY_NOT_SHARED flag if you do not wish to
+ * share geometry among primitives with the same parameters.
+ */
+public class Cone extends Primitive {
+ float radius, height;
+ int xdivisions, ydivisions;
+ static final int MID_REZ_DIV_X = 15;
+ static final int MID_REZ_DIV_Y = 1;
+
+ /**
+ * Designates the body of the cone. Used by <code>getShape</code>.
+ *
+ * @see Cone#getShape
+ */
+ public static final int BODY = 0;
+
+ /**
+ * Designates the end-cap of the cone. Used by <code>getShape</code>.
+ *
+ * @see Cone#getShape
+ */
+ public static final int CAP = 1;
+
+ /**
+ * Constructs a default Cone of radius of 1.0 and height
+ * of 2.0. Resolution defaults to 15 divisions along X and axis
+ * and 1 along the Y axis. Normals are generated, texture
+ * coordinates are not.
+ */
+ public Cone(){
+ this(1.0f, 2.0f, GENERATE_NORMALS, MID_REZ_DIV_X, MID_REZ_DIV_Y, null);
+ }
+
+ /**
+ *
+ * Constructs a default Cone of a given radius and height. Normals
+ * are generated, texture coordinates are not.
+ * @param radius Radius
+ * @param height Height
+ */
+ public Cone (float radius, float height)
+ {
+ this(radius, height, GENERATE_NORMALS, MID_REZ_DIV_X, MID_REZ_DIV_Y, null);
+ }
+
+ /**
+ *
+ * Constructs a default cone of a given radius, height,
+ * and appearance. Normals are generated, texture coordinates are not.
+ * @param radius Radius
+ * @param height Height
+ * @param ap Appearance
+ *
+ * @since Java 3D 1.2.1
+ */
+ public Cone (float radius, float height, Appearance ap)
+ {
+ this(radius, height, GENERATE_NORMALS, MID_REZ_DIV_X, MID_REZ_DIV_Y, ap);
+ }
+
+ /**
+ *
+ * Constructs a default cone of a given radius, height,
+ * primitive flags, and appearance.
+ * @param radius Radius
+ * @param height Height
+ * @param primflags Primitive flags
+ * @param ap Appearance
+ */
+ public Cone (float radius, float height, int primflags, Appearance ap)
+ {
+ this(radius, height, primflags, MID_REZ_DIV_X, MID_REZ_DIV_Y, ap);
+ }
+
+ /**
+ * Obtains the Shape3D node associated with one of the parts of the
+ * cone (the body or the cap). This allows users to modify the appearance
+ * or geometry of individual parts.
+ * @param partId The part to return (BODY or CAP).
+ * @return The Shape3D object associated with the partId. If an
+ * invalid partId is passed in, null is returned.
+ */
+ public Shape3D getShape(int partId){
+ if (partId > CAP || partId < BODY) return null;
+ return (Shape3D)getChild(partId);
+ }
+
+
+ /**
+ * Sets appearance of the cone. This will set each part of the
+ * cone (cap & body) to the same appearance. To set each
+ * part's appearance separately, use getShape(partId) to get the
+ * individual shape and call shape.setAppearance(ap).
+ */
+ public void setAppearance(Appearance ap){
+ ((Shape3D)getChild(BODY)).setAppearance(ap);
+ ((Shape3D)getChild(CAP)).setAppearance(ap);
+ }
+
+ /**
+ * Gets the appearance of the specified part of the cone.
+ *
+ * @param partId identifier for a given subpart of the cone
+ *
+ * @return The appearance object associated with the partID. If an
+ * invalid partId is passed in, null is returned.
+ *
+ * @since Java 3D 1.2.1
+ */
+ public Appearance getAppearance(int partId) {
+ if (partId > CAP || partId < BODY) return null;
+ return getShape(partId).getAppearance();
+ }
+
+
+ /**
+ * Constructs a customized Cone of a given radius, height, flags,
+ * resolution (X and Y dimensions), and appearance. The
+ * resolution is defined in terms of number of subdivisions
+ * along the object's X axis (width) and Y axis (height). More divisions
+ * lead to finer tesselated objects.
+ * <p>
+ * If appearance is null, the default white appearance will be used.
+ * @param radius Radius
+ * @param height Height
+ * @param xdivision Number of divisions along X direction.
+ * @param ydivision Number of divisions along the height of cone.
+ * @param primflags flags
+ * @param ap Appearance
+ */
+
+ public Cone(float radius, float height, int primflags,
+ int xdivision, int ydivision,
+ Appearance ap)
+ {
+ super();
+
+ Shape3D shape[] = new Shape3D[2];
+ this.radius = radius;
+ this.height = height;
+ xdivisions = xdivision;
+ ydivisions = ydivision;
+ flags = primflags;
+ boolean outside = (flags & GENERATE_NORMALS_INWARD) == 0;
+ Quadrics q = new Quadrics();
+ GeomBuffer gbuf = null;
+
+ GeomBuffer cache = getCachedGeometry(Primitive.CONE,
+ radius, 0.0f, height,
+ xdivision, ydivision, primflags);
+ if (cache != null){
+// System.out.println("using cached geometry");
+ shape[BODY] = new Shape3D(cache.getComputedGeometry());
+ numVerts += cache.getNumVerts();
+ numTris += cache.getNumTris();
+ }
+ else {
+ // the body of the cone consists of the top of the cone and if
+ // ydivisions is greater than 1, the body of the cone.
+ gbuf = q.coneTop((double)(height/2.0 - height/ydivisions),
+ (double)(radius/ydivisions), height/ydivisions,
+ xdivisions, 1.0-1.0/(double)ydivisions,
+ outside);
+ shape[BODY] = new Shape3D(gbuf.getGeom(flags));
+ numVerts += gbuf.getNumVerts();
+ numTris += gbuf.getNumTris();
+ if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
+ cacheGeometry(Primitive.CONE,
+ radius, 0.0f, height,
+ xdivision, ydivision, primflags, gbuf);
+ }
+ }
+
+ // only need to add a body if the ydivisions is greater than 1
+ if (ydivisions > 1) {
+ cache = getCachedGeometry(Primitive.CONE_DIVISIONS, radius, 0.0f,
+ height, xdivision, ydivision, primflags);
+ if (cache != null) {
+// System.out.println("using cached divisions");
+ shape[BODY].addGeometry(cache.getComputedGeometry());
+ numVerts += cache.getNumVerts();
+ numTris += cache.getNumTris();
+ }
+ else {
+ gbuf = q.coneBody(-(double)(height/2.0),
+ (double)(height/2.0-height/ydivisions),
+ (double)radius, (double)(radius/ydivisions),
+ xdivisions, ydivisions-1, 1.0/(double)ydivisions,
+ outside);
+ shape[BODY].addGeometry(gbuf.getGeom(flags));
+ numVerts += gbuf.getNumVerts();
+ numTris += gbuf.getNumTris();
+ if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
+ cacheGeometry(Primitive.CONE_DIVISIONS, radius, 0.0f, height,
+ xdivision, ydivision, primflags, gbuf);
+ }
+ }
+ }
+
+ if ((flags & ENABLE_APPEARANCE_MODIFY) != 0) {
+ (shape[BODY]).setCapability(Shape3D.ALLOW_APPEARANCE_READ);
+ (shape[BODY]).setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
+ }
+
+ if ((flags & ENABLE_GEOMETRY_PICKING) != 0) {
+ (shape[BODY]).setCapability(Shape3D.ALLOW_GEOMETRY_READ);
+ }
+
+ this.addChild(shape[BODY]);
+
+ // Create bottom cap.
+ cache = getCachedGeometry(Primitive.BOTTOM_DISK, radius,
+ radius, -height/2.0f, xdivision, xdivision,
+ primflags);
+ if (cache != null) {
+// System.out.println("using cached bottom");
+ shape[CAP] = new Shape3D(cache.getComputedGeometry());
+ numVerts += cache.getNumVerts();
+ numTris += cache.getNumTris();
+ }
+ else {
+ gbuf = q.disk((double)radius, xdivision, -(double)height/2.0,
+ !outside);
+ shape[CAP] = new Shape3D(gbuf.getGeom(flags));
+ numVerts += gbuf.getNumVerts();
+ numTris += gbuf.getNumTris();
+ if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
+ cacheGeometry(Primitive.BOTTOM_DISK, radius, radius, -height/2.0f,
+ xdivision, xdivision, primflags, gbuf);
+ }
+ }
+
+ if ((flags & ENABLE_APPEARANCE_MODIFY) != 0) {
+ (shape[CAP]).setCapability(Shape3D.ALLOW_APPEARANCE_READ);
+ (shape[CAP]).setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
+ }
+
+ if ((flags & ENABLE_GEOMETRY_PICKING) != 0) {
+ (shape[CAP]).setCapability(Shape3D.ALLOW_GEOMETRY_READ);
+ }
+
+// Transform3D t2 = new Transform3D();
+
+ // Flip it to match up the texture coords.
+/* This causes the bottom not to match up for odd xdivision values
+ objectMat = new Matrix4d();
+ objectMat.setIdentity();
+ rotMat = new Matrix4d();
+ rotMat.setIdentity();
+ rotMat.rotZ(Math.PI);
+ objectMat.mul(objectMat, rotMat);
+ t2.set(objectMat);
+*/
+
+ this.addChild(shape[CAP]);
+
+ if (ap == null){
+ setAppearance();
+ }
+ else setAppearance(ap);
+ }
+
+ /**
+ * Used to create a new instance of the node. This routine is called
+ * by <code>cloneTree</code> to duplicate the current node.
+ * <code>cloneNode</code> should be overridden by any user subclassed
+ * objects. All subclasses must have their <code>cloneNode</code>
+ * method consist of the following lines:
+ * <P><blockquote><pre>
+ * public Node cloneNode(boolean forceDuplicate) {
+ * UserSubClass usc = new UserSubClass();
+ * usc.duplicateNode(this, forceDuplicate);
+ * return usc;
+ * }
+ * </pre></blockquote>
+ * @param forceDuplicate when set to <code>true</code>, causes the
+ * <code>duplicateOnCloneTree</code> flag to be ignored. When
+ * <code>false</code>, the value of each node's
+ * <code>duplicateOnCloneTree</code> variable determines whether
+ * NodeComponent data is duplicated or copied.
+ *
+ * @see Node#cloneTree
+ * @see Node#duplicateNode
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public Node cloneNode(boolean forceDuplicate) {
+ Cone c = new Cone(radius, height, flags, xdivisions,
+ ydivisions, getAppearance());
+ c.duplicateNode(this, forceDuplicate);
+ return c;
+ }
+
+ /**
+ * Copies all node information from <code>originalNode</code> into
+ * the current node. This method is called from the
+ * <code>cloneNode</code> method which is, in turn, called by the
+ * <code>cloneTree</code> method.
+ * <P>
+ * For any <i>NodeComponent</i> objects
+ * contained by the object being duplicated, each <i>NodeComponent</i>
+ * object's <code>duplicateOnCloneTree</code> value is used to determine
+ * whether the <i>NodeComponent</i> should be duplicated in the new node
+ * or if just a reference to the current node should be placed in the
+ * new node. This flag can be overridden by setting the
+ * <code>forceDuplicate</code> parameter in the <code>cloneTree</code>
+ * method to <code>true</code>.
+ *
+ * @param originalNode the original node to duplicate.
+ * @param forceDuplicate when set to <code>true</code>, causes the
+ * <code>duplicateOnCloneTree</code> flag to be ignored. When
+ * <code>false</code>, the value of each node's
+ * <code>duplicateOnCloneTree</code> variable determines whether
+ * NodeComponent data is duplicated or copied.
+ *
+ * @see Node#cloneTree
+ * @see Node#cloneNode
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public void duplicateNode(Node originalNode, boolean forceDuplicate) {
+ super.duplicateNode(originalNode, forceDuplicate);
+ }
+
+ /**
+ * Returns the radius of the cone
+ *
+ * @since Java 3D 1.2.1
+ */
+ public float getRadius() {
+ return radius;
+ }
+
+ /**
+ * Returns the height of the cone
+ *
+ * @since Java 3D 1.2.1
+ */
+ public float getHeight() {
+ return height;
+ }
+
+ /**
+ * Returns the number divisions along the X direction
+ *
+ * @since Java 3D 1.2.1
+ */
+ public int getXdivisions() {
+ return xdivisions;
+ }
+
+ /**
+ * Returns the number of divisions along the height of the cone
+ *
+ * @since Java 3D 1.2.1
+ */
+ public int getYdivisions() {
+ return ydivisions;
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Cylinder.java b/src/classes/share/com/sun/j3d/utils/geometry/Cylinder.java
new file mode 100644
index 0000000..64e4ab1
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Cylinder.java
@@ -0,0 +1,421 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry;
+
+import com.sun.j3d.utils.geometry.*;
+import java.io.*;
+import java.util.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+/**
+ * Cylinder is a geometry primitive defined with a radius and a height.
+ * It is a capped cylinder centered at the origin with its central axis
+ * aligned along the Y-axis.
+ * <p>
+ * When a texture is applied to a cylinder, the texture is applied to the
+ * caps and the body different. A texture is mapped CCW from the back of the
+ * body. The top and bottom caps are mapped such that the texture appears
+ * front facing when the caps are rotated 90 degrees toward the viewer.
+ * <p>
+ * By default all primitives with the same parameters share their
+ * geometry (e.g., you can have 50 shperes in your scene, but the
+ * geometry is stored only once). A change to one primitive will
+ * effect all shared nodes. Another implication of this
+ * implementation is that the capabilities of the geometry are shared,
+ * and once one of the shared nodes is live, the capabilities cannot
+ * be set. Use the GEOMETRY_NOT_SHARED flag if you do not wish to
+ * share geometry among primitives with the same parameters.
+ */
+
+public class Cylinder extends Primitive{
+ float radius, height;
+ int xdivisions, ydivisions;
+
+ static final int MID_REZ_DIV_X = 15;
+ static final int MID_REZ_DIV_Y = 1;
+
+ /**
+ * Designates the body of the cylinder. Used by <code>getShape</code>.
+ *
+ * @see Cylinder#getShape
+ */
+ public static final int BODY = 0;
+
+ /**
+ * Designates the top end-cap of the cylinder.
+ * Used by <code>getShape</code>.
+ *
+ * @see Cylinder#getShape
+ */
+ public static final int TOP = 1;
+
+ /**
+ * Designates the bottom end-cap of the cylinder.
+ * Used by <code>getShape</code>.
+ *
+ * @see Cylinder#getShape
+ */
+ public static final int BOTTOM = 2;
+
+ /**
+ * Constructs a default cylinder of radius of 1.0 and height
+ * of 2.0. Normals are generated by default, texture
+ * coordinates are not. Resolution defaults to 15 divisions
+ * along X axis and 1 along the Y axis.
+ */
+ public Cylinder() {
+ this(1.0f, 2.0f, GENERATE_NORMALS, MID_REZ_DIV_X, MID_REZ_DIV_Y, null);
+ }
+
+ /**
+ * Constructs a default cylinder of a given radius and height.
+ * Normals are generated by default, texture coordinates are not.
+ * @param radius Radius
+ * @param height Height
+ */
+ public Cylinder (float radius, float height) {
+ this(radius, height, GENERATE_NORMALS, MID_REZ_DIV_X, MID_REZ_DIV_Y,
+ null);
+ }
+
+ /**
+ * Constructs a default cylinder of a given radius, height, and
+ * appearance. Normals are generated by default, texture
+ * coordinates are not.
+ * @param radius Radius
+ * @param height Height
+ * @param ap Appearance
+ */
+ public Cylinder (float radius, float height, Appearance ap)
+ {
+ this(radius, height, GENERATE_NORMALS, MID_REZ_DIV_X, MID_REZ_DIV_Y,
+ ap);
+ }
+
+ /**
+ *
+ * Constructs a default cylinder of a given radius, height,
+ * primitive flags and appearance.
+ * @param radius Radius
+ * @param height Height
+ * @param primflags Flags
+ * @param ap Appearance
+ */
+ public Cylinder (float radius, float height, int primflags, Appearance ap)
+ {
+ this(radius, height, primflags, MID_REZ_DIV_X, MID_REZ_DIV_Y, ap);
+ }
+
+ /**
+ * Obtains the Shape3D node associated with a given part of the cylinder.
+ * This allows users to modify the appearance or geometry
+ * of individual parts.
+ * @param partId The part to return (BODY, TOP, or BOTTOM).
+ * @return The Shape3D object associated with the partID. If an
+ * invalid partId is passed in, null is returned.
+ */
+ public Shape3D getShape(int partId){
+ if (partId > BOTTOM || partId < BODY) return null;
+ return (Shape3D)getChild(partId);
+ }
+
+ /** Sets appearance of the cylinder. This will set each part of the
+ * cylinder (TOP,BOTTOM,BODY) to the same appearance. To set each
+ * part's appearance separately, use getShape(partId) to get the
+ * individual shape and call shape.setAppearance(ap).
+ */
+ public void setAppearance(Appearance ap) {
+ ((Shape3D)getChild(BODY)).setAppearance(ap);
+ ((Shape3D)getChild(TOP)).setAppearance(ap);
+ ((Shape3D)getChild(BOTTOM)).setAppearance(ap);
+ }
+
+ /**
+ * Gets the appearance of the specified part of the cylinder.
+ *
+ * @param partId identifier for a given subpart of the cylinder
+ *
+ * @return The appearance object associated with the partID. If an
+ * invalid partId is passed in, null is returned.
+ *
+ * @since Java 3D 1.2.1
+ */
+ public Appearance getAppearance(int partId) {
+ if (partId > BOTTOM || partId < BODY) return null;
+ return getShape(partId).getAppearance();
+ }
+
+
+ /**
+ * Constructs a customized cylinder of a given radius, height,
+ * resolution (X and Y dimensions), and appearance. The
+ * resolution is defined in terms of number of subdivisions
+ * along the object's X axis (width) and Y axis (height). More divisions
+ * lead to more finely tesselated objects.
+ * @param radius Radius
+ * @param height Height
+ * @param xdivision Number of divisions along X direction.
+ * @param ydivision Number of divisions along height of cylinder.
+ * @param primflags Primitive flags.
+ * @param ap Appearance
+ */
+ public Cylinder(float radius, float height, int primflags,
+ int xdivision, int ydivision, Appearance ap) {
+ super();
+
+ this.radius = radius;
+ this.height = height;
+ this.xdivisions = xdivision;
+ this.ydivisions = ydivision;
+ flags = primflags;
+ boolean outside = (flags & GENERATE_NORMALS_INWARD) == 0;
+ // Create many body of the cylinder.
+ Quadrics q = new Quadrics();
+ GeomBuffer gbuf = null;
+ Shape3D shape[] = new Shape3D[3];
+
+ GeomBuffer cache = getCachedGeometry(Primitive.CYLINDER,
+ (float)BODY, radius, height,
+ xdivision, ydivision, primflags);
+ if (cache != null){
+// System.out.println("using cached geometry");
+ shape[BODY] = new Shape3D(cache.getComputedGeometry());
+ numVerts += cache.getNumVerts();
+ numTris += cache.getNumTris();
+ }
+ else {
+ gbuf = q.cylinder((double)height, (double)radius,
+ xdivision, ydivision, outside);
+ shape[BODY] = new Shape3D(gbuf.getGeom(flags));
+ numVerts += gbuf.getNumVerts();
+ numTris += gbuf.getNumTris();
+ if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0)
+ cacheGeometry(Primitive.CYLINDER,
+ (float)BODY, radius, height,
+ xdivision, ydivision, primflags, gbuf);
+ }
+
+ if ((flags & ENABLE_APPEARANCE_MODIFY) != 0) {
+ (shape[BODY]).setCapability(Shape3D.ALLOW_APPEARANCE_READ);
+ (shape[BODY]).setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
+ }
+
+ if ((flags & ENABLE_GEOMETRY_PICKING) != 0) {
+ (shape[BODY]).setCapability(Shape3D.ALLOW_GEOMETRY_READ);
+ }
+
+ this.addChild(shape[BODY]);
+
+ // Create top of cylinder
+ cache = getCachedGeometry(Primitive.TOP_DISK, radius, radius,
+ height/2.0f, xdivision, xdivision, primflags);
+ if (cache != null) {
+// System.out.println("using cached top");
+ shape[TOP] = new Shape3D(cache.getComputedGeometry());
+ numVerts += cache.getNumVerts();
+ numTris += cache.getNumTris();
+ }
+ else {
+ gbuf = q.disk((double)radius, xdivision, height/2.0,
+ outside);
+ shape[TOP] = new Shape3D(gbuf.getGeom(flags));
+ numVerts += gbuf.getNumVerts();
+ numTris += gbuf.getNumTris();
+ if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
+ cacheGeometry(Primitive.TOP_DISK, radius, radius,
+ height/2.0f, xdivision, xdivision,
+ primflags, gbuf);
+ }
+ }
+
+ if ((flags & ENABLE_APPEARANCE_MODIFY) != 0) {
+ (shape[TOP]).setCapability(Shape3D.ALLOW_APPEARANCE_READ);
+ (shape[TOP]).setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
+ }
+
+ if ((flags & ENABLE_GEOMETRY_PICKING) != 0) {
+ (shape[TOP]).setCapability(Shape3D.ALLOW_GEOMETRY_READ);
+ }
+
+ this.addChild(shape[TOP]);
+
+ // Create bottom
+ cache = getCachedGeometry(Primitive.BOTTOM_DISK, radius, radius,
+ -height/2.0f, xdivision, xdivision,
+ primflags);
+ if (cache != null) {
+// System.out.println("using cached bottom");
+ shape[BOTTOM] = new Shape3D(cache.getComputedGeometry());
+ numVerts += cache.getNumVerts();
+ numTris += cache.getNumTris();
+ }
+ else {
+ gbuf = q.disk((double)radius, xdivision, -height/2.0, !outside);
+ shape[BOTTOM] = new Shape3D(gbuf.getGeom(flags));
+ numVerts += gbuf.getNumVerts();
+ numTris += gbuf.getNumTris();
+ if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
+ cacheGeometry(Primitive.BOTTOM_DISK, radius, radius,
+ -height/2.0f, xdivision, xdivision,
+ primflags, gbuf);
+ }
+ }
+
+ if ((flags & ENABLE_APPEARANCE_MODIFY) != 0) {
+ (shape[BOTTOM]).setCapability(Shape3D.ALLOW_APPEARANCE_READ);
+ (shape[BOTTOM]).setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
+ }
+
+ if ((flags & ENABLE_GEOMETRY_PICKING) != 0) {
+ (shape[BOTTOM]).setCapability(Shape3D.ALLOW_GEOMETRY_READ);
+ }
+
+ this.addChild(shape[BOTTOM]);
+
+ // Set Appearance
+ if (ap == null){
+ setAppearance();
+ }
+ else setAppearance(ap);
+ }
+
+ /**
+ * Used to create a new instance of the node. This routine is called
+ * by <code>cloneTree</code> to duplicate the current node.
+ * <code>cloneNode</code> should be overridden by any user subclassed
+ * objects. All subclasses must have their <code>cloneNode</code>
+ * method consist of the following lines:
+ * <P><blockquote><pre>
+ * public Node cloneNode(boolean forceDuplicate) {
+ * UserSubClass usc = new UserSubClass();
+ * usc.duplicateNode(this, forceDuplicate);
+ * return usc;
+ * }
+ * </pre></blockquote>
+ * @param forceDuplicate when set to <code>true</code>, causes the
+ * <code>duplicateOnCloneTree</code> flag to be ignored. When
+ * <code>false</code>, the value of each node's
+ * <code>duplicateOnCloneTree</code> variable determines whether
+ * NodeComponent data is duplicated or copied.
+ *
+ * @see Node#cloneTree
+ * @see Node#duplicateNode
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public Node cloneNode(boolean forceDuplicate) {
+ Cylinder c = new Cylinder(radius, height, flags, xdivisions,
+ ydivisions, getAppearance());
+ c.duplicateNode(this, forceDuplicate);
+ return c;
+ }
+
+ /**
+ * Copies all node information from <code>originalNode</code> into
+ * the current node. This method is called from the
+ * <code>cloneNode</code> method which is, in turn, called by the
+ * <code>cloneTree</code> method.
+ * <P>
+ * For any <i>NodeComponent</i> objects
+ * contained by the object being duplicated, each <i>NodeComponent</i>
+ * object's <code>duplicateOnCloneTree</code> value is used to determine
+ * whether the <i>NodeComponent</i> should be duplicated in the new node
+ * or if just a reference to the current node should be placed in the
+ * new node. This flag can be overridden by setting the
+ * <code>forceDuplicate</code> parameter in the <code>cloneTree</code>
+ * method to <code>true</code>.
+ *
+ * @param originalNode the original node to duplicate.
+ * @param forceDuplicate when set to <code>true</code>, causes the
+ * <code>duplicateOnCloneTree</code> flag to be ignored. When
+ * <code>false</code>, the value of each node's
+ * <code>duplicateOnCloneTree</code> variable determines whether
+ * NodeComponent data is duplicated or copied.
+ *
+ * @see Node#cloneTree
+ * @see Node#cloneNode
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public void duplicateNode(Node originalNode, boolean forceDuplicate) {
+ super.duplicateNode(originalNode, forceDuplicate);
+ }
+
+ /**
+ * Returns the radius of the cylinder
+ *
+ * @since Java 3D 1.2.1
+ */
+ public float getRadius() {
+ return radius;
+ }
+
+ /**
+ * Returns the height of the cylinder
+ *
+ * @since Java 3D 1.2.1
+ */
+ public float getHeight() {
+ return height;
+ }
+
+ /**
+ * Returns the number divisions along the X direction
+ *
+ * @since Java 3D 1.2.1
+ */
+ public int getXdivisions() {
+ return xdivisions;
+ }
+
+ /**
+ * Returns the number of divisions along the height of the cylinder
+ *
+ * @since Java 3D 1.2.1
+ */
+ public int getYdivisions() {
+ return ydivisions;
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Degenerate.java b/src/classes/share/com/sun/j3d/utils/geometry/Degenerate.java
new file mode 100644
index 0000000..a7c40da
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Degenerate.java
@@ -0,0 +1,170 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+import java.io.*;
+import java.util.*;
+import javax.vecmath.*;
+
+class Degenerate {
+
+ /**
+ * This function checks whether the triangle i1, i2, i3 is an ear, where
+ * the vertex i4 lies on at least one of the two edges i1, i2 or i3, i1.
+ * basically, we can cut the polygon at i4 into two pieces. the polygon
+ * touches at i4 back to back if following the next-pointers in one
+ * subpolygon and following the prev-pointers in the other subpolygon yields
+ * the same orientation for both subpolygons. otherwise, i4 forms a
+ * bottle neck of the polygon, and i1, i2, i3 is no valid ear.
+ *
+ * Note that this function may come up with the incorrect answer if the
+ * polygon has self-intersections.
+ */
+ static boolean handleDegeneracies(Triangulator triRef, int i1, int ind1, int i2,
+ int i3, int i4, int ind4) {
+ int i0, i5;
+ int type[] = new int[1];
+ int ind0, ind2, ind5;
+ boolean flag;
+ double area = 0.0, area1 = 0, area2 = 0.0;
+
+ /* assert(InPointsList(i1));
+ assert(InPointsList(i2));
+ assert(InPointsList(i3));
+ assert(InPointsList(i4));
+ */
+
+ // first check whether the successor or predecessor of i4 is inside the
+ // triangle, or whether any of the two edges incident at i4 intersects
+ // i2, i3.
+ ind5 = triRef.fetchPrevData(ind4);
+ i5 = triRef.fetchData(ind5);
+
+ // assert(ind4 != ind5);
+ //assert(InPointsList(i5));
+ if ((i5 != i2) && (i5 != i3)) {
+ flag = Numerics.vtxInTriangle(triRef, i1, i2, i3, i5, type);
+ if (flag && (type[0] == 0)) return true;
+ if (i2 <= i3) {
+ if (i4 <= i5)
+ flag = Numerics.segIntersect(triRef, i2, i3, i4, i5, -1);
+ else
+ flag = Numerics.segIntersect(triRef, i2, i3, i5, i4, -1);
+ }
+ else {
+ if (i4 <= i5)
+ flag = Numerics.segIntersect(triRef, i3, i2, i4, i5, -1);
+ else
+ flag = Numerics.segIntersect(triRef, i3, i2, i5, i4, -1);
+ }
+ if (flag)
+ return true;
+ }
+
+ ind5 = triRef.fetchNextData(ind4);
+ i5 = triRef.fetchData(ind5);
+ // assert(ind4 != ind5);
+ // assert(InPointsList(i5));
+ if ((i5 != i2) && (i5 != i3)) {
+ flag = Numerics.vtxInTriangle(triRef, i1, i2, i3, i5, type);
+ if (flag && (type[0] == 0)) return true;
+ if (i2 <= i3) {
+ if (i4 <= i5) flag = Numerics.segIntersect(triRef, i2, i3, i4, i5, -1);
+ else flag = Numerics.segIntersect(triRef, i2, i3, i5, i4, -1);
+ }
+ else {
+ if (i4 <= i5) flag = Numerics.segIntersect(triRef, i3, i2, i4, i5, -1);
+ else flag = Numerics.segIntersect(triRef, i3, i2, i5, i4, -1);
+ }
+ if (flag) return true;
+ }
+
+ i0 = i1;
+ ind0 = ind1;
+ ind1 = triRef.fetchNextData(ind1);
+ i1 = triRef.fetchData(ind1);
+ while (ind1 != ind4) {
+ ind2 = triRef.fetchNextData(ind1);
+ i2 = triRef.fetchData(ind2);
+ area = Numerics.stableDet2D(triRef, i0, i1, i2);
+ area1 += area;
+ ind1 = ind2;
+ i1 = i2;
+ }
+
+ ind1 = triRef.fetchPrevData(ind0);
+ i1 = triRef.fetchData(ind1);
+ while (ind1 != ind4) {
+ ind2 = triRef.fetchPrevData(ind1);
+ i2 = triRef.fetchData(ind2);
+ area = Numerics.stableDet2D(triRef, i0, i1, i2);
+ area2 += area;
+ ind1 = ind2;
+ i1 = i2;
+ }
+
+ if (Numerics.le(area1, triRef.ZERO) && Numerics.le(area2, triRef.ZERO))
+ return false;
+ else if (Numerics.ge(area1, triRef.ZERO) && Numerics.ge(area2, triRef.ZERO))
+ return false;
+ else
+ return true;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Desperate.java b/src/classes/share/com/sun/j3d/utils/geometry/Desperate.java
new file mode 100644
index 0000000..a9185ff
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Desperate.java
@@ -0,0 +1,431 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+import java.io.*;
+import java.util.*;
+import javax.vecmath.*;
+
+class Desperate {
+
+ /**
+ * the functions in this file try to ensure that we always end up with
+ * something that (topologically) is a triangulation.
+ *
+ * the more desperate we get, the more aggressive means we choose for making
+ * diagonals "valid".
+ */
+ static boolean desperate(Triangulator triRef, int ind, int i, boolean[] splitted) {
+ int[] i1 = new int[1];
+ int[] i2 = new int[1];
+ int[] i3 = new int[1];
+ int[] i4 = new int[1];
+ int[] ind1 = new int[1];
+ int[] ind2 = new int[1];
+ int[] ind3 = new int[1];
+ int[] ind4 = new int[1];
+
+ splitted[0] = false;
+
+ // check whether there exist consecutive vertices i1, i2, i3, i4 such
+ // that i1, i2 and i3, i4 intersect
+ if (existsCrossOver(triRef, ind, ind1, i1, ind2, i2, ind3, i3, ind4, i4)) {
+ // insert two new diagonals around the cross-over without checking
+ // whether they are intersection-free
+ handleCrossOver(triRef, ind1[0], i1[0], ind2[0], i2[0], ind3[0], i3[0],
+ ind4[0], i4[0]);
+ return false;
+ }
+
+ NoHash.prepareNoHashEdges(triRef, i, i+1);
+
+ // check whether there exists a valid diagonal that splits the polygon
+ // into two parts
+ if (existsSplit(triRef, ind, ind1, i1, ind2, i2)) {
+ // break up the polygon by inserting this diagonal (which can't be an
+ // ear -- otherwise, we would not have ended up in this part of the
+ // code). then, let's treat the two polygons separately. hopefully,
+ // this will help to handle self-overlapping polygons in the "correct"
+ // way.
+ handleSplit(triRef, ind1[0], i1[0], ind2[0], i2[0]);
+ splitted[0] = true;
+ return false;
+ }
+
+ return true;
+ }
+
+
+ static boolean existsCrossOver(Triangulator triRef, int ind, int[] ind1, int[] i1,
+ int[] ind2, int[] i2, int[] ind3, int[] i3,
+ int[] ind4, int[] i4) {
+ BBox bb1, bb2;
+
+ ind1[0] = ind;
+ i1[0] = triRef.fetchData(ind1[0]);
+ ind2[0] = triRef.fetchNextData(ind1[0]);
+ i2[0] = triRef.fetchData(ind2[0]);
+ ind3[0] = triRef.fetchNextData(ind2[0]);
+ i3[0] = triRef.fetchData(ind3[0]);
+ ind4[0] = triRef.fetchNextData(ind3[0]);
+ i4[0] = triRef.fetchData(ind4[0]);
+
+ do {
+ bb1 = new BBox(triRef, i1[0], i2[0]);
+ bb2 = new BBox(triRef, i3[0], i4[0]);
+ if (bb1.BBoxOverlap(bb2)) {
+ if (Numerics.segIntersect(triRef, bb1.imin, bb1.imax, bb2.imin, bb2.imax, -1))
+ return true;
+ }
+ ind1[0] = ind2[0];
+ i1[0] = i2[0];
+ ind2[0] = ind3[0];
+ i2[0] = i3[0];
+ ind3[0] = ind4[0];
+ i3[0] = i4[0];
+ ind4[0] = triRef.fetchNextData(ind3[0]);
+ i4[0] = triRef.fetchData(ind4[0]);
+
+ } while (ind1[0] != ind);
+
+ return false;
+ }
+
+
+ static void handleCrossOver(Triangulator triRef, int ind1, int i1, int ind2,
+ int i2, int ind3, int i3, int ind4, int i4) {
+ double ratio1, ratio4;
+ boolean first;
+ int angle1, angle4;
+
+ // which pair of triangles shall I insert?? we can use either i1, i2, i3
+ // and i1, i3, i4, or we can use i2, i3, i4 and i1, i2, i4...
+ angle1 = triRef.getAngle(ind1);
+ angle4 = triRef.getAngle(ind4);
+ if (angle1 < angle4) first = true;
+ else if (angle1 > angle4) first = false;
+ else if (triRef.earsSorted) {
+ ratio1 = Numerics.getRatio(triRef, i3, i4, i1);
+ ratio4 = Numerics.getRatio(triRef, i1, i2, i4);
+ if (ratio4 < ratio1) first = false;
+ else first = true;
+ }
+ else {
+ first = true;
+ }
+
+ if (first) {
+ // first clip i1, i2, i3, then clip i1, i3, i4
+ triRef.deleteLinks(ind2);
+ // StoreTriangle(GetOriginal(ind1), GetOriginal(ind2), GetOriginal(ind3));
+ triRef.storeTriangle(ind1, ind2, ind3);
+ triRef.setAngle(ind3, 1);
+ Heap.insertIntoHeap(triRef, 0.0, ind3, ind1, ind4);
+ }
+ else {
+ // first clip i2, i3, i4, then clip i1, i2, i4
+ triRef.deleteLinks(ind3);
+ //StoreTriangle(GetOriginal(ind2), GetOriginal(ind3), GetOriginal(ind4));
+ triRef.storeTriangle(ind2, ind3, ind4);
+ triRef.setAngle(ind2, 1);
+ Heap.insertIntoHeap(triRef, 0.0, ind2, ind1, ind4);
+ }
+ }
+
+
+ static boolean letsHope(Triangulator triRef, int ind) {
+ int ind0, ind1, ind2;
+ int i0, i1, i2;
+
+ // let's clip the first convex corner. of course, we know that this is no
+ // ear in an ideal world. but this polygon isn't ideal, either!
+ ind1 = ind;
+ i1 = triRef.fetchData(ind1);
+
+ do {
+ if (triRef.getAngle(ind1) > 0) {
+ ind0 = triRef.fetchPrevData(ind1);
+ i0 = triRef.fetchData(ind0);
+ ind2 = triRef.fetchNextData(ind1);
+ i2 = triRef.fetchData(ind2);
+ Heap.insertIntoHeap(triRef, 0.0, ind1, ind0, ind2);
+ return true;
+ }
+ ind1 = triRef.fetchNextData(ind1);
+ i1 = triRef.fetchData(ind1);
+ } while (ind1 != ind);
+
+ // no convex corners? so, let's cheat! this code won't stop without some
+ // triangulation... ;-) g-i-g-o? right! perhaps, this is what you
+ // call a robust code?!
+ triRef.setAngle(ind, 1);
+ ind0 = triRef.fetchPrevData(ind);
+ i0 = triRef.fetchData(ind0);
+ ind2 = triRef.fetchNextData(ind);
+ i2 = triRef.fetchData(ind2);
+ Heap.insertIntoHeap(triRef, 0.0, ind, ind0, ind2);
+ i1 = triRef.fetchData(ind);
+
+ return true;
+
+ // see, we never have to return "false"...
+ /*
+ return false;
+ */
+ }
+
+
+ static boolean existsSplit(Triangulator triRef, int ind, int[] ind1, int[] i1,
+ int[] ind2, int[] i2) {
+ int ind3, ind4, ind5;
+ int i3, i4, i5;
+
+ if (triRef.numPoints > triRef.maxNumDist) {
+ // System.out.println("Desperate: Expanding distances array ...");
+ triRef.maxNumDist = triRef.numPoints;
+ triRef.distances = new Distance[triRef.maxNumDist];
+ for (int k = 0; k < triRef.maxNumDist; k++)
+ triRef.distances[k] = new Distance();
+ }
+ ind1[0] = ind;
+ i1[0] = triRef.fetchData(ind1[0]);
+ ind4 = triRef.fetchNextData(ind1[0]);
+ i4 = triRef.fetchData(ind4);
+ // assert(*ind1 != ind4);
+ ind5 = triRef.fetchNextData(ind4);
+ i5 = triRef.fetchData(ind5);
+ // assert(*ind1 != *ind2);
+ ind3 = triRef.fetchPrevData(ind1[0]);
+ i3 = triRef.fetchData(ind3);
+ // assert(*ind2 != ind3);
+ if (foundSplit(triRef, ind5, i5, ind3, ind1[0], i1[0], i3, i4, ind2, i2))
+ return true;
+ i3 = i1[0];
+ ind1[0] = ind4;
+ i1[0] = i4;
+ ind4 = ind5;
+ i4 = i5;
+ ind5 = triRef.fetchNextData(ind4);
+ i5 = triRef.fetchData(ind5);
+
+ while (ind5 != ind) {
+ if (foundSplit(triRef, ind5, i5, ind, ind1[0], i1[0], i3, i4, ind2, i2))
+ return true;
+ i3 = i1[0];
+ ind1[0] = ind4;
+ i1[0] = i4;
+ ind4 = ind5;
+ i4 = i5;
+ ind5 = triRef.fetchNextData(ind4);
+ i5 = triRef.fetchData(ind5);
+ }
+
+ return false;
+ }
+
+
+ /**
+ * This function computes the winding number of a polygon with respect to a
+ * point p. no care is taken to handle cases where p lies on the
+ * boundary of the polygon. (this is no issue in our application, as we will
+ * always compute the winding number with respect to the mid-point of a
+ * valid diagonal.)
+ */
+ static int windingNumber(Triangulator triRef, int ind, Point2f p) {
+ double angle;
+ int ind2;
+ int i1, i2, number;
+
+ i1 = triRef.fetchData(ind);
+ ind2 = triRef.fetchNextData(ind);
+ i2 = triRef.fetchData(ind2);
+ angle = Numerics.angle(triRef, p, triRef.points[i1], triRef.points[i2]);
+ while (ind2 != ind) {
+ i1 = i2;
+ ind2 = triRef.fetchNextData(ind2);
+ i2 = triRef.fetchData(ind2);
+ angle += Numerics.angle(triRef, p, triRef.points[i1], triRef.points[i2]);
+ }
+
+ angle += Math.PI;
+ number = (int)(angle / (Math.PI*2.0));
+
+ return number;
+ }
+
+
+
+
+ static boolean foundSplit(Triangulator triRef, int ind5, int i5, int ind, int ind1,
+ int i1, int i3, int i4, int[] ind2, int[] i2) {
+ Point2f center;
+ int numDist = 0;
+ int j, i6, i7;
+ int ind6, ind7;
+ BBox bb;
+ boolean convex, coneOk;
+
+ // Sort the points according to their distance from i1
+ do {
+ // assert(numDist < triRef.maxNumDist);
+ triRef.distances[numDist].dist = Numerics.baseLength(triRef.points[i1],
+ triRef.points[i5]);
+ triRef.distances[numDist].ind = ind5;
+ ++numDist;
+ ind5 = triRef.fetchNextData(ind5);
+ i5 = triRef.fetchData(ind5);
+ } while (ind5 != ind);
+
+ Bridge.sortDistance(triRef.distances, numDist);
+
+ // find a valid diagonal.
+ for (j = 0; j < numDist; ++j) {
+ ind2[0] = triRef.distances[j].ind;
+ i2[0] = triRef.fetchData(ind2[0]);
+ if (i1 != i2[0]) {
+ ind6 = triRef.fetchPrevData(ind2[0]);
+ i6 = triRef.fetchData(ind6);
+ ind7 = triRef.fetchNextData(ind2[0]);
+ i7 = triRef.fetchData(ind7);
+
+ convex = triRef.getAngle(ind2[0]) > 0;
+ coneOk = Numerics.isInCone(triRef, i6, i2[0], i7, i1, convex);
+ if (coneOk) {
+ convex = triRef.getAngle(ind1) > 0;
+ coneOk = Numerics.isInCone(triRef, i3, i1, i4, i2[0], convex);
+ if (coneOk) {
+ bb = new BBox(triRef, i1, i2[0]);
+ if (!NoHash.noHashEdgeIntersectionExists(triRef, bb, -1, -1, ind1, -1)) {
+ // check whether this is a good diagonal; we do not want a
+ // diagonal that may create figure-8's!
+ center = new Point2f();
+ Basic.vectorAdd2D(triRef.points[i1], triRef.points[i2[0]], center);
+ Basic.multScalar2D(0.5, center);
+ if (windingNumber(triRef, ind, center) == 1) return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+
+ static void handleSplit(Triangulator triRef, int ind1, int i1, int ind3, int i3) {
+ int ind2, ind4, prev, next;
+ int prv, nxt, angle;
+ int vIndex, comIndex = -1;
+
+ // duplicate nodes in order to form end points of the new diagonal
+ ind2 = triRef.makeNode(i1);
+ triRef.insertAfter(ind1, ind2);
+
+ // Need to get the original data, before setting it.
+
+ comIndex = triRef.list[ind1].getCommonIndex();
+
+ triRef.list[ind2].setCommonIndex(comIndex);
+
+ ind4 = triRef.makeNode(i3);
+ triRef.insertAfter(ind3, ind4);
+
+ comIndex = triRef.list[ind3].getCommonIndex();
+ triRef.list[ind4].setCommonIndex(comIndex);
+
+ // insert the diagonal into the boundary loop, thus splitting the loop
+ // into two loops
+ triRef.splitSplice(ind1, ind2, ind3, ind4);
+
+ // store pointers to the two new loops
+ triRef.storeChain(ind1);
+ triRef.storeChain(ind3);
+
+ // reset the angles
+ next = triRef.fetchNextData(ind1);
+ nxt = triRef.fetchData(next);
+ prev = triRef.fetchPrevData(ind1);
+ prv = triRef.fetchData(prev);
+ angle = Numerics.isConvexAngle(triRef, prv, i1, nxt, ind1);
+ triRef.setAngle(ind1, angle);
+
+ next = triRef.fetchNextData(ind2);
+ nxt = triRef.fetchData(next);
+ prev = triRef.fetchPrevData(ind2);
+ prv = triRef.fetchData(prev);
+ angle = Numerics.isConvexAngle(triRef, prv, i1, nxt, ind2);
+ triRef.setAngle(ind2, angle);
+
+ next = triRef.fetchNextData(ind3);
+ nxt = triRef.fetchData(next);
+ prev = triRef.fetchPrevData(ind3);
+ prv = triRef.fetchData(prev);
+ angle = Numerics.isConvexAngle(triRef, prv, i3, nxt, ind3);
+ triRef.setAngle(ind3, angle);
+
+ next = triRef.fetchNextData(ind4);
+ nxt = triRef.fetchData(next);
+ prev = triRef.fetchPrevData(ind4);
+ prv = triRef.fetchData(prev);
+ angle = Numerics.isConvexAngle(triRef, prv, i3, nxt, ind4);
+ triRef.setAngle(ind4, angle);
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Distance.java b/src/classes/share/com/sun/j3d/utils/geometry/Distance.java
new file mode 100644
index 0000000..9772db6
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Distance.java
@@ -0,0 +1,73 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+class Distance extends Object {
+ int ind;
+ double dist;
+
+ Distance() {
+ }
+
+ void copy(Distance d) {
+ ind = d.ind;
+ dist = d.dist;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/EarClip.java b/src/classes/share/com/sun/j3d/utils/geometry/EarClip.java
new file mode 100644
index 0000000..b5f78f0
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/EarClip.java
@@ -0,0 +1,348 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+import java.io.*;
+import java.util.*;
+import javax.vecmath.*;
+
+class EarClip {
+
+ /**
+ * Classifies all the internal angles of the loop referenced by ind.
+ * the following classification is used:
+ * 0 ... if angle is 180 degrees
+ * 1 ... if angle between 0 and 180 degrees
+ * 2 ... if angle is 0 degrees
+ * -1 ... if angle between 180 and 360 degrees
+ * -2 ... if angle is 360 degrees
+ */
+ static void classifyAngles(Triangulator triRef, int ind) {
+ int ind0, ind1, ind2;
+ int i0, i1, i2;
+ int angle;
+
+ ind1 = ind;
+ i1 = triRef.fetchData(ind1);
+ ind0 = triRef.fetchPrevData(ind1);
+ i0 = triRef.fetchData(ind0);
+
+ do {
+ ind2 = triRef.fetchNextData(ind1);
+ i2 = triRef.fetchData(ind2);
+ angle = Numerics.isConvexAngle(triRef, i0, i1, i2, ind1);
+ triRef.setAngle(ind1, angle);
+ i0 = i1;
+ i1 = i2;
+ ind1 = ind2;
+ } while (ind1 != ind);
+
+ }
+
+
+ static void classifyEars(Triangulator triRef, int ind) {
+ int ind1;
+ int i1;
+ int[] ind0, ind2;
+ double[] ratio;
+
+ ind0 = new int[1];
+ ind2 = new int[1];
+ ratio = new double[1];
+
+ Heap.initHeap(triRef);
+
+ ind1 = ind;
+ i1 = triRef.fetchData(ind1);
+
+ do {
+ if ((triRef.getAngle(ind1) > 0) &&
+ isEar(triRef, ind1, ind0, ind2, ratio)) {
+
+ Heap.dumpOnHeap(triRef, ratio[0], ind1, ind0[0], ind2[0]);
+ }
+ ind1 = triRef.fetchNextData(ind1);
+ i1 = triRef.fetchData(ind1);
+ } while (ind1 != ind);
+
+ // Not using sorted_ear so don't have to do MakeHeap();
+ // MakeHeap();
+
+ // Heap.printHeapData(triRef);
+
+ }
+
+
+ /**
+ * This function checks whether a diagonal is valid, that is, whether it is
+ * locally within the polygon, and whether it does not intersect any other
+ * segment of the polygon. also, some degenerate cases get a special
+ * handling.
+ */
+ static boolean isEar(Triangulator triRef, int ind2, int[] ind1, int[] ind3,
+ double[] ratio) {
+ int i0, i1, i2, i3, i4;
+ int ind0, ind4;
+ BBox bb;
+ boolean convex, coneOk;
+
+ i2 = triRef.fetchData(ind2);
+ ind3[0] = triRef.fetchNextData(ind2);
+ i3 = triRef.fetchData(ind3[0]);
+ ind4 = triRef.fetchNextData(ind3[0]);
+ i4 = triRef.fetchData(ind4);
+ ind1[0] = triRef.fetchPrevData(ind2);
+ i1 = triRef.fetchData(ind1[0]);
+ ind0 = triRef.fetchPrevData(ind1[0]);
+ i0 = triRef.fetchData(ind0);
+
+ /*
+ System.out.println("isEar : i0 " + i0 + " i1 " + i1 + " i2 " + i2 +
+ " i3 " + i3 + " i4 " + i4);
+ */
+
+ if ((i1 == i3) || (i1 == i2) || (i2 == i3) || (triRef.getAngle(ind2) == 2)) {
+ // oops, this is not a simple polygon!
+ ratio[0] = 0.0;
+ return true;
+ }
+
+ if (i0 == i3) {
+ // again, this is not a simple polygon!
+ if ((triRef.getAngle(ind0) < 0) || (triRef.getAngle(ind3[0]) < 0)) {
+ ratio[0] = 0.0;
+ return true;
+ }
+ else
+ return false;
+ }
+
+ if (i1 == i4) {
+ // again, this is not a simple polygon!
+ if ((triRef.getAngle(ind1[0]) < 0) || (triRef.getAngle(ind4) < 0)) {
+ ratio[0] = 0.0;
+ return true;
+ }
+ else
+ return false;
+ }
+
+ // check whether the new diagonal i1, i3 locally is within the polygon
+ convex = triRef.getAngle(ind1[0]) > 0;
+ coneOk = Numerics.isInCone(triRef, i0, i1, i2, i3, convex);
+ // System.out.println("isEar :(1) convex " + convex + " coneOk " + coneOk );
+
+ if (!coneOk) return false;
+ convex = triRef.getAngle(ind3[0]) > 0;
+ coneOk = Numerics.isInCone(triRef, i2, i3, i4, i1, convex);
+ // System.out.println("isEar :(2) convex " + convex + " coneOk " + coneOk );
+
+ if (coneOk) {
+ // check whether this diagonal is a valid diagonal. this translates to
+ // checking either condition CE1 or CE2 (see my paper). If CE1 is to
+ // to be checked, then we use a BV-tree or a grid. Otherwise, we use
+ // "buckets" (i.e., a grid) or no hashing at all.
+ bb = new BBox(triRef, i1, i3);
+ // use CE2 + no_hashing
+ if(!NoHash.noHashIntersectionExists(triRef, i2, ind2, i3, i1, bb)) {
+ if (triRef.earsSorted) {
+ // determine the quality of the triangle
+ ratio[0] = Numerics.getRatio(triRef, i1, i3, i2);
+ }
+ else {
+ ratio[0] = 1.0;
+ }
+ return true;
+ }
+ }
+
+ // System.out.println("isEar : false");
+ return false;
+ }
+
+
+
+ /**
+ * This is the main function that drives the ear-clipping. it obtains an ear
+ * from set of ears maintained in a priority queue, clips this ear, and
+ * updates all data structures appropriately. (ears are arranged in the
+ * priority queue (i.e., heap) according to a quality criterion that tries
+ * to avoid skinny triangles.)
+ */
+ static boolean clipEar(Triangulator triRef, boolean[] done) {
+
+ int ind0, ind1, ind3, ind4;
+
+ int i0, i1, i2, i3, i4;
+ int angle1, angle3;
+
+ double ratio[] = new double[1];
+ int index0[] = new int[1];
+ int index1[] = new int[1];
+ int index2[] = new int[1];
+ int index3[] = new int[1];
+ int index4[] = new int[1];
+ int ind2[] = new int[1];
+
+ int testCnt = 0;
+
+ // Heap.printHeapData(triRef);
+
+ do {
+
+ // System.out.println("In clipEarloop " + testCnt++);
+
+ if (!Heap.deleteFromHeap(triRef, ind2, index1, index3))
+ // no ear exists?!
+ return false;
+
+ // get the successors and predecessors in the list of nodes and check
+ // whether the ear still is part of the boundary
+ ind1 = triRef.fetchPrevData(ind2[0]);
+ i1 = triRef.fetchData(ind1);
+ ind3 = triRef.fetchNextData(ind2[0]);
+ i3 = triRef.fetchData(ind3);
+
+ } while ((index1[0] != ind1) || (index3[0] != ind3));
+
+ //System.out.println("Out of clipEarloop ");
+
+ i2 = triRef.fetchData(ind2[0]);
+
+ // delete the clipped ear from the list of nodes, and update the bv-tree
+ triRef.deleteLinks(ind2[0]);
+
+ // store the ear in a list of ears which have already been clipped
+ // StoreTriangle(GetOriginal(ind1), GetOriginal(ind2), GetOriginal(ind3));
+ triRef.storeTriangle(ind1, ind2[0], ind3);
+
+ /* */
+ /* update the angle classification at ind1 and ind3 */
+ /* */
+ ind0 = triRef.fetchPrevData(ind1);
+ i0 = triRef.fetchData(ind0);
+ if (ind0 == ind3) {
+ // nothing left
+ done[0] = true;
+ return true;
+ }
+ angle1 = Numerics.isConvexAngle(triRef, i0, i1, i3, ind1);
+
+ ind4 = triRef.fetchNextData(ind3);
+ i4 = triRef.fetchData(ind4);
+
+ angle3 = Numerics.isConvexAngle(triRef, i1, i3, i4, ind3);
+
+ if (i1 != i3) {
+ if ((angle1 >= 0) && (triRef.getAngle(ind1) < 0))
+ NoHash.deleteReflexVertex(triRef, ind1);
+ if ((angle3 >= 0) && (triRef.getAngle(ind3) < 0))
+ NoHash.deleteReflexVertex(triRef, ind3);
+ }
+ else {
+ if ((angle1 >= 0) && (triRef.getAngle(ind1) < 0))
+ NoHash.deleteReflexVertex(triRef, ind1);
+ else if ((angle3 >= 0) && (triRef.getAngle(ind3) < 0))
+ NoHash.deleteReflexVertex(triRef, ind3);
+
+ }
+
+ triRef.setAngle(ind1, angle1);
+ triRef.setAngle(ind3, angle3);
+
+ // check whether either of ind1 and ind3 is an ear. (the "ratio" is
+ // the length of the triangle's longest side divided by the length of the
+ // height normal onto this side; it is used as a quality criterion.)
+ if (angle1 > 0) {
+ if (isEar(triRef, ind1, index0, index2, ratio)) {
+ // insert the new ear into the priority queue of ears
+ Heap.insertIntoHeap(triRef, ratio[0], ind1, index0[0], index2[0]);
+ }
+ }
+
+ if (angle3 > 0) {
+ if(isEar(triRef, ind3, index2, index4, ratio)) {
+ Heap.insertIntoHeap(triRef, ratio[0], ind3, index2[0], index4[0]);
+ }
+ }
+
+ // check whether the triangulation is finished.
+ ind0 = triRef.fetchPrevData(ind1);
+ i0 = triRef.fetchData(ind0);
+ ind4 = triRef.fetchNextData(ind3);
+ i4 = triRef.fetchData(ind4);
+ if (ind0 == ind4) {
+ // only one triangle left -- clip it!
+ triRef.storeTriangle(ind1, ind3, ind4);
+ done[0] = true;
+ }
+ else {
+ done[0] = false;
+ }
+
+ return true;
+ }
+
+}
+
+
+
+
+
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Edge.java b/src/classes/share/com/sun/j3d/utils/geometry/Edge.java
new file mode 100644
index 0000000..6e56f47
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Edge.java
@@ -0,0 +1,89 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry;
+
+// Class created so that the two vertex indices that make up an
+// edge can be hashed.
+class Edge {
+
+ public int v1;
+ public int v2;
+ private static final int HASHCONST = 0xEDCBA987;
+
+ public int hashCode()
+ {
+ return ((v1 * HASHCONST) << 2) ^ (v2 * HASHCONST);
+ } // end of Edge.hashCode
+
+ public boolean equals(Object x)
+ {
+ if (!(x instanceof Edge)) return false;
+ Edge e = (Edge)x;
+ return (v1 == e.v1) && (v2 == e.v2);
+ } // End of Edge.equals
+
+ public String toString()
+ {
+ return "(" + v1 + ", " + v2 + ")";
+ } // End of toString
+
+ public Edge(int a, int b)
+ {
+ v1 = a;
+ v2 = b;
+ }
+
+ public Edge(Edge e)
+ {
+ v1 = e.v1;
+ v2 = e.v2;
+ }
+
+ public Edge()
+ {
+ }
+} // end of class Edge
+
+// End of file Edge.java
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/EdgeTable.java b/src/classes/share/com/sun/j3d/utils/geometry/EdgeTable.java
new file mode 100644
index 0000000..5a33da5
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/EdgeTable.java
@@ -0,0 +1,116 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Set;
+import com.sun.j3d.utils.geometry.Edge;
+
+class EdgeTable {
+
+ private HashMap edgeTable;
+ private static final int DEBUG = 0;
+
+
+
+ Integer get(int a, int b)
+ {
+ return (Integer)edgeTable.get(new Edge(a, b));
+ } // End of get()
+
+
+ Integer get(Edge e)
+ {
+ return (Integer)edgeTable.get(e);
+ } // End of get()
+
+
+
+ // This function creates a table used to connect the triangles.
+ // Here's how it works. If a triangle is made of indices 12,
+ // 40, and 51, then edge(12, 40) gets 51, edge(40, 51) gets 12,
+ // and edge(51, 12) gets 40. This lets us quickly move from
+ // triangle to triangle without saving a lot of extra data.
+ EdgeTable(int triangleIndices[])
+ {
+ // We'll have one edge for each vertex
+ edgeTable = new HashMap(triangleIndices.length * 2);
+
+ // Fill in table
+ Edge e;
+ for (int t = 0 ; t < triangleIndices.length ; t += 3) {
+ // Put all 3 edges of triangle into table
+ for (int v = 0 ; v < 3 ; v++) {
+ e = new Edge(triangleIndices[t + v],
+ triangleIndices[t + ((v + 1) % 3)]);
+
+ if (edgeTable.get(e) != null) {
+ if ((DEBUG & 1) != 0) {
+ System.out.println("EdgeTable Error: duplicate edge (" +
+ triangleIndices[t + v] + ", " +
+ triangleIndices[t + ((v + 1) % 3)] + ").");
+ }
+ } else {
+ // Store index of 3rd vertex (across from edge)
+ edgeTable.put(e, new Integer(t + ((v + 2) % 3)));
+ }
+ }
+ }
+
+ if ((DEBUG & 1) != 0) {
+ System.out.println("Edge Table:");
+ Iterator list = edgeTable.keySet().iterator();
+ while (list.hasNext()) {
+ Edge edge = (Edge)list.next();
+ System.out.println(" (" + edge.v1 + ", " + edge.v2 + ") = " +
+ get(edge.v1, edge.v2));
+ }
+ }
+ } // End of constructor EdgeTable
+
+} // End of class EdgeTable
+
+// End of file EdgeTable.java
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/GeomBuffer.java b/src/classes/share/com/sun/j3d/utils/geometry/GeomBuffer.java
new file mode 100644
index 0000000..48dd0fb
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/GeomBuffer.java
@@ -0,0 +1,557 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry;
+
+import com.sun.j3d.utils.geometry.*;
+import java.io.*;
+import java.util.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import java.math.*;
+
+/**
+ * GeomBuffer allows OpenGL-like input of geometry data. It outputs
+ * Java 3D geometry array objects. This utility is to simplify porting
+ * of OpenGL programs to Java 3D.
+ *<p>
+ * Here is a sample code that use this utility to create some quads.
+ * <P><blockquote><pre>
+ *
+ * GeomBuffer gbuf = new GeomBuffer(100);
+ * gbuf.begin(GeomBuffer.QUADS);
+ *
+ * for (int i = 0; i < 5; i++){
+ * gbuf.normal3d(0.0, 1.0, 0.0);
+ * gbuf.vertex3d(1.0, 1.0, 0.0);
+ *
+ * gbuf.normal3d(0.0, 1.0, 0.0);
+ * gbuf.vertex3d(0.0, 1.0, 0.0);
+ *
+ * gbuf.normal3d(0.0, 1.0, 0.0);
+ * gbuf.vertex3d(0.0, 0.0, 0.0);
+ *
+ * gbuf.normal3d(0.0, 1.0, 0.0);
+ * gbuf.vertex3d(1.0, 0.0, 0.0);
+ * }
+ * gbuf.end();
+ * Shape3D shape = new Shape3D(gbuf.getGeom(GeomBuffer.GENERATE_NORMALS));
+ * </pre></blockquote>
+ * Notice, that you only need to specify some upperbound on the number of
+ * points you'll use at the beginning (100 in this case).
+ * <p>
+ * Currently, you are limited to one primitive type per geom buffer. Future
+ * versions will add support for mixed primitive types.
+ *
+ **/
+
+class GeomBuffer extends Object{
+
+ //Supported Primitives
+ static final int QUAD_STRIP = 0x01;
+ static final int TRIANGLES = 0x02;
+ static final int QUADS = 0x04;
+ static final int TRIANGLE_FAN = 0x10;
+ static final int TRIANGLE_STRIP = 0x20;
+
+ private int flags;
+ static final int GENERATE_NORMALS = 0x01;
+ static final int GENERATE_TEXTURE_COORDS = 0x02;
+
+ Point3f[] pts = null;
+ Vector3f[] normals = null;
+ TexCoord2f[] tcoords = null;
+ int currVertCnt;
+ int currPrimCnt;
+ int[] currPrimType = null,
+ currPrimStartVertex = null,
+ currPrimEndVertex = null;
+ GeometryArray geometry;
+ int numVerts = 0;
+ int numTris = 0;
+ int numTexUnit = 1;
+ int texCoordSetMap[] = null;
+
+
+ static final int debug = 0;
+
+ /** Creates a geometry buffer of given number of vertices
+ * @param numVerts total number of vertices to allocate by this buffer.
+ * This is an upper bound estimate.
+ */
+ GeomBuffer(int numVerts, int numTexUnit)
+ {
+ this.numTexUnit = numTexUnit;
+ pts = new Point3f[numVerts];
+ normals = new Vector3f[numVerts];
+ tcoords = new TexCoord2f[numVerts];
+ // max primitives is numV/3
+ currPrimType = new int[numVerts/3];
+ currPrimStartVertex = new int[numVerts/3];
+ currPrimEndVertex = new int[numVerts/3];
+ currVertCnt = 0;
+ currPrimCnt = 0;
+
+ texCoordSetMap = new int[numTexUnit];
+ for (int i = 0; i < numTexUnit; i++)
+ texCoordSetMap[i] = 0;
+
+ }
+
+ GeomBuffer(int numVerts)
+ {
+ this(numVerts, 1);
+ }
+
+ /*
+ * Returns a Java 3D geometry array from the geometry buffer. You need to
+ * call begin, vertex3d, end, etc. before calling this, of course.
+ *
+ * @param format vertex format.
+ */
+
+ GeometryArray getGeom(int format)
+ {
+ GeometryArray obj = null;
+ flags = format;
+
+ numTris = 0;
+
+ //Switch based on first primitive.
+ switch (currPrimType[0]){
+ case TRIANGLES:
+ obj = processTriangles();
+ break;
+ case QUADS:
+ obj = processQuads();
+ break;
+ case QUAD_STRIP:
+ case TRIANGLE_STRIP:
+ obj = processQuadStrips();
+ break;
+ case TRIANGLE_FAN:
+ obj = processTriangleFan();
+ break;
+ }
+ if ((obj != null) && ((flags & Primitive.ENABLE_GEOMETRY_PICKING) != 0)) {
+ obj.setCapability(Geometry.ALLOW_INTERSECT);
+ obj.setCapability(GeometryArray.ALLOW_FORMAT_READ);
+ obj.setCapability(GeometryArray.ALLOW_COUNT_READ);
+ obj.setCapability(GeometryArray.ALLOW_COORDINATE_READ);
+ }
+ return obj;
+ }
+
+
+ /**
+ * Begins a new primitive given the primitive type.
+ *
+ * @param prim the primitive type (listed above).
+ *
+ **/
+
+ void begin(int prim)
+ {
+ if (debug >= 1) System.out.println("quad");
+ currPrimType[currPrimCnt] = prim;
+ currPrimStartVertex[currPrimCnt] = currVertCnt;
+ }
+
+
+ /**
+ * End of primitive.
+ *
+ *
+ **/
+ void end()
+ {
+ if (debug >= 1) System.out.println("end");
+ currPrimEndVertex[currPrimCnt] = currVertCnt;
+ currPrimCnt++;
+ }
+
+ void vertex3d(double x, double y, double z)
+ {
+
+ if (debug >= 2) System.out.println("v " + x + " " +
+ y + " " +
+ z);
+ pts[currVertCnt] = new Point3f((float)x, (float)y, (float)z);
+ currVertCnt++;
+ }
+
+ void normal3d(double x, double y, double z)
+ {
+ if (debug >= 2) System.out.println("n " + x + " " +
+ y + " " + z);
+ double sum = x*x+y*y+z*z;
+ if (Math.abs(sum - 1.0) > 0.001){
+ if (debug >= 2) System.out.println("normalizing");
+ double root = Math.sqrt(sum);
+ if (root > 0.000001) {
+ x /= root;
+ y /= root;
+ z /= root;
+ } else {
+ y = z = 0.0; x = 1.0;
+ }
+ }
+ normals[currVertCnt] = new Vector3f((float)x, (float)y, (float)z);
+ }
+
+ void texCoord2d(double s, double t)
+ {
+ if (debug >= 2) System.out.println("t " +
+ s + " " +
+ t);
+ tcoords[currVertCnt] = new TexCoord2f((float)s, (float)t);
+ }
+
+ /**
+ * Returns the Java 3D geometry gotten from calling getGeom.
+ *
+ **/
+
+ GeometryArray getComputedGeometry()
+ {
+ return geometry;
+ }
+
+ int getNumTris()
+ {
+ return numTris;
+ }
+
+ int getNumVerts()
+ {
+ return numVerts;
+ }
+
+
+ private GeometryArray processQuadStrips()
+ {
+ GeometryArray obj = null;
+ int i;
+ int totalVerts = 0;
+
+ // Calculate how many vertices needed to hold all of the individual quads
+ int stripCounts[] = new int[currPrimCnt];
+ for (i = 0; i < currPrimCnt; i++){
+ stripCounts[i] = currPrimEndVertex[i] - currPrimStartVertex[i];
+ totalVerts += stripCounts[i];
+ }
+
+ if (debug >= 1) System.out.println("totalVerts " + totalVerts);
+
+ int tsaFlags = TriangleStripArray.COORDINATES;
+ if ((flags & GENERATE_NORMALS) != 0)
+ tsaFlags |= TriangleStripArray.NORMALS;
+ if ((flags & GENERATE_TEXTURE_COORDS) != 0)
+ tsaFlags |= TriangleStripArray.TEXTURE_COORDINATE_2;
+
+ // Create GeometryArray to pass back
+ obj = new TriangleStripArray(totalVerts, tsaFlags,
+ 1, texCoordSetMap, stripCounts);
+
+ // Allocate space to store new vertex info
+ Point3f[] newpts = new Point3f[totalVerts];
+ Vector3f[] newnormals = new Vector3f[totalVerts];
+ TexCoord2f[] newtcoords = new TexCoord2f[totalVerts];
+ int currVert = 0;
+
+ // Repeat for each Quad Strip
+ for (i = 0; i < currPrimCnt; i++){
+ // Output order for these quad arrays same as java 3d triangle strips
+ for (int j = currPrimStartVertex[i] ; j < currPrimEndVertex[i] ; j++){
+ outVertex(newpts, newnormals, newtcoords, currVert++,
+ pts, normals, tcoords, j);
+ }
+
+ }
+
+ numVerts = currVert;
+ numTris += totalVerts - currPrimCnt * 2;
+
+ obj.setCoordinates(0, newpts);
+ if ((flags & GENERATE_NORMALS) != 0)
+ obj.setNormals(0, newnormals);
+ if ((flags & GENERATE_TEXTURE_COORDS) != 0)
+ obj.setTextureCoordinates(0, 0, newtcoords);
+
+ geometry = obj;
+ return obj;
+ }
+
+ private GeometryArray processQuads()
+ {
+ GeometryArray obj = null;
+ int i;
+ int totalVerts = 0;
+
+ for (i = 0; i < currPrimCnt; i++){
+ totalVerts += currPrimEndVertex[i] - currPrimStartVertex[i];
+ }
+
+ if (debug >= 1) System.out.println("totalVerts " + totalVerts);
+
+ if (((flags & GENERATE_NORMALS) != 0) &&
+ ((flags & GENERATE_TEXTURE_COORDS) != 0)){
+ obj = new QuadArray(totalVerts,
+ QuadArray.COORDINATES |
+ QuadArray.NORMALS |
+ QuadArray.TEXTURE_COORDINATE_2,
+ 1, texCoordSetMap);
+ }
+ else
+ if (((flags & GENERATE_NORMALS) == 0) &&
+ ((flags & GENERATE_TEXTURE_COORDS) != 0)){
+ obj = new QuadArray(totalVerts,
+ QuadArray.COORDINATES |
+ QuadArray.TEXTURE_COORDINATE_2,
+ 1, texCoordSetMap);
+ }
+ else
+ if (((flags & GENERATE_NORMALS) != 0) &&
+ ((flags & GENERATE_TEXTURE_COORDS) == 0)){
+ obj = new QuadArray(totalVerts,
+ QuadArray.COORDINATES |
+ QuadArray.NORMALS);
+ }
+ else {
+ obj = new QuadArray(totalVerts,
+ QuadArray.COORDINATES);
+ }
+
+ Point3f[] newpts = new Point3f[totalVerts];
+ Vector3f[] newnormals = new Vector3f[totalVerts];
+ TexCoord2f[] newtcoords = new TexCoord2f[totalVerts];
+ int currVert = 0;
+
+ if (debug > 1) System.out.println("total prims " + currPrimCnt);
+
+ for (i = 0; i < currPrimCnt; i++){
+ if (debug > 1) System.out.println("start " + currPrimStartVertex[i] +
+ " end " + currPrimEndVertex[i]);
+ for (int j = currPrimStartVertex[i]; j < currPrimEndVertex[i] - 3;j+=4){
+ outVertex(newpts, newnormals, newtcoords, currVert++,
+ pts, normals, tcoords, j);
+ outVertex(newpts, newnormals, newtcoords, currVert++,
+ pts, normals, tcoords, j + 1);
+ outVertex(newpts, newnormals, newtcoords, currVert++,
+ pts, normals, tcoords, j + 2);
+ outVertex(newpts, newnormals, newtcoords, currVert++,
+ pts, normals, tcoords, j + 3);
+ numTris += 2;
+ }
+ }
+ numVerts = currVert;
+
+ obj.setCoordinates(0, newpts);
+ if ((flags & GENERATE_NORMALS) != 0)
+ obj.setNormals(0, newnormals);
+ if ((flags & GENERATE_TEXTURE_COORDS) != 0)
+ obj.setTextureCoordinates(0, 0, newtcoords);
+
+ geometry = obj;
+ return obj;
+ }
+
+ private GeometryArray processTriangles()
+ {
+ GeometryArray obj = null;
+ int i;
+ int totalVerts = 0;
+
+ for (i = 0; i < currPrimCnt; i++){
+ totalVerts += currPrimEndVertex[i] - currPrimStartVertex[i];
+ }
+
+ if (debug >= 1) System.out.println("totalVerts " + totalVerts);
+
+ if (((flags & GENERATE_NORMALS) != 0) &&
+ ((flags & GENERATE_TEXTURE_COORDS) != 0)){
+ obj = new TriangleArray(totalVerts,
+ TriangleArray.COORDINATES |
+ TriangleArray.NORMALS |
+ TriangleArray.TEXTURE_COORDINATE_2,
+ 1, texCoordSetMap);
+ }
+ else
+ if (((flags & GENERATE_NORMALS) == 0) &&
+ ((flags & GENERATE_TEXTURE_COORDS) != 0)){
+ obj = new TriangleArray(totalVerts,
+ TriangleArray.COORDINATES |
+ TriangleArray.TEXTURE_COORDINATE_2,
+ 1, texCoordSetMap);
+ }
+ else
+ if (((flags & GENERATE_NORMALS) != 0) &&
+ ((flags & GENERATE_TEXTURE_COORDS) == 0)){
+ obj = new TriangleArray(totalVerts,
+ TriangleArray.COORDINATES |
+ TriangleArray.NORMALS);
+ }
+ else {
+ obj = new TriangleArray(totalVerts,
+ TriangleArray.COORDINATES);
+ }
+
+ Point3f[] newpts = new Point3f[totalVerts];
+ Vector3f[] newnormals = new Vector3f[totalVerts];
+ TexCoord2f[] newtcoords = new TexCoord2f[totalVerts];
+ int currVert = 0;
+
+ for (i = 0; i < currPrimCnt; i++){
+ for (int j = currPrimStartVertex[i]; j < currPrimEndVertex[i] - 2;j+=3){
+ outVertex(newpts, newnormals, newtcoords, currVert++,
+ pts, normals, tcoords, j);
+ outVertex(newpts, newnormals, newtcoords, currVert++,
+ pts, normals, tcoords, j + 1);
+ outVertex(newpts, newnormals, newtcoords, currVert++,
+ pts, normals, tcoords, j + 2);
+ numTris += 1;
+ }
+ }
+ numVerts = currVert;
+
+ obj.setCoordinates(0, newpts);
+ if ((flags & GENERATE_NORMALS) != 0)
+ obj.setNormals(0, newnormals);
+ if ((flags & GENERATE_TEXTURE_COORDS) != 0)
+ obj.setTextureCoordinates(0, 0, newtcoords);
+
+ geometry = obj;
+ return obj;
+ }
+
+
+ private GeometryArray processTriangleFan() {
+ if (debug > 0) System.out.println("processTriangleFan");
+
+ GeometryArray obj = null;
+ int i;
+ int totalVerts = 0;
+
+ int stripCounts[] = new int[currPrimCnt];
+
+ // figure out how many vertices we need to hold the individual fans
+ for (i = 0; i < currPrimCnt; i++) {
+ stripCounts[i] = currPrimEndVertex[i] - currPrimStartVertex[i];
+ totalVerts += stripCounts[i];
+ }
+
+ // figure out what flags we need
+ int tfFlags = TriangleFanArray.COORDINATES;
+ if ((flags & GENERATE_NORMALS) != 0) {
+ tfFlags |= TriangleFanArray.NORMALS;
+ }
+ if ((flags & GENERATE_TEXTURE_COORDS) != 0) {
+ tfFlags |= TriangleFanArray.TEXTURE_COORDINATE_2;
+ }
+
+ // create the TriangleFanArray
+ obj = new TriangleFanArray(totalVerts, tfFlags, 1, texCoordSetMap,
+ stripCounts);
+
+ // allocate space for vertex info
+ Point3f[] newpts = new Point3f[totalVerts];
+ Vector3f[] newnormals = new Vector3f[totalVerts];
+ TexCoord2f[] newtcoords = new TexCoord2f[totalVerts];
+
+ int currVert = 0;
+
+ // repeat for each fan
+ for (i = 0; i < currPrimCnt; i++) {
+ for (int j = currPrimStartVertex[i]; j < currPrimEndVertex[i]; j++) {
+ outVertex(newpts, newnormals, newtcoords, currVert++, pts,
+ normals, tcoords, j);
+ }
+ }
+
+ for (i = 0; i < newpts.length; i++) {
+ if (debug > 1) System.out.println("i = " + i + " " + newpts[i]);
+ }
+
+ numVerts = currVert;
+ numTris = totalVerts - currPrimCnt * 2;
+
+ // set the coordinates on the GeometryArray
+ obj.setCoordinates(0, newpts);
+
+ // set the normals and tex coords if necessary
+ if ((flags & GENERATE_NORMALS) != 0) {
+ obj.setNormals(0, newnormals);
+ }
+ if ((flags & GENERATE_TEXTURE_COORDS) != 0) {
+ obj.setTextureCoordinates(0, 0, newtcoords);
+ }
+ geometry = obj;
+ return obj;
+ }
+
+
+
+ void outVertex(Point3f[] dpts, Vector3f[] dnormals, TexCoord2f[] dtcoords,
+ int dloc,
+ Point3f[] spts, Vector3f[] snormals, TexCoord2f[] stcoords,
+ int sloc)
+ {
+ if (debug >= 1) System.out.println("v " + spts[sloc].x + " " +
+ spts[sloc].y + " " +
+ spts[sloc].z);
+
+ // PSP: Do we really need new points here?
+
+ dpts[dloc] = new Point3f(spts[sloc]);
+
+ if ((flags & GENERATE_NORMALS) != 0){
+ dnormals[dloc] = new Vector3f(snormals[sloc]);
+ }
+ if ((flags & GENERATE_TEXTURE_COORDS) != 0){
+ if (debug >= 2) System.out.println("final out tcoord");
+ dtcoords[dloc] = new TexCoord2f(stcoords[sloc]);
+ }
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/GeometryInfo.java b/src/classes/share/com/sun/j3d/utils/geometry/GeometryInfo.java
new file mode 100644
index 0000000..69eeadb
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/GeometryInfo.java
@@ -0,0 +1,2826 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry;
+
+import com.sun.j3d.utils.geometry.Triangulator;
+import java.io.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import com.sun.j3d.internal.J3dUtilsI18N;
+import java.util.HashMap;
+import com.sun.j3d.utils.geometry.GeometryInfoGenerator;
+import com.sun.j3d.internal.BufferWrapper;
+import com.sun.j3d.internal.ByteBufferWrapper;
+import com.sun.j3d.internal.FloatBufferWrapper;
+import com.sun.j3d.internal.DoubleBufferWrapper;
+import com.sun.j3d.internal.ByteOrderWrapper;
+import javax.media.j3d.J3DBuffer;
+
+/**
+ * The GeometryInfo object holds data for processing by the Java3D geometry
+ * utility tools.<p><blockquote>
+ *
+ * The NormalGenerator adds normals to geometry without normals.<p>
+ *
+ * The Stripifier combines adjacent triangles into triangle strips for
+ * more efficent rendering.<p></blockquote>
+ *
+ * Also, the GeometryCompressor can take a set of GeometryInfo objects in a
+ * CompressionSteam and generate a CompressedGeometry object from the
+ * geometry.<p>
+ * Geometry is loaded into a GeometryInfo in a manner similar to the
+ * <a href="../../../../../javax/media/j3d/GeometryArray.html">
+ * GeometryArray</a> methods. The constructor for the GeometryInfo takes a flag
+ * that specifies the kind of data being loaded. The vertex data is
+ * specified using methods that are similar to the GeometryArray methods, but
+ * with fewer variations.<p>
+ * The major difference between GeometryInfo and GeometryArray is
+ * that the number of vertices, vertex format, and other data are specified
+ * implictly, rather than as part of the constructor. The number of verticies
+ * comes from the number of coordinates passed to the setCoordinates()
+ * method. The format comes from the set of data components that are
+ * specified. For example, calling the setCoordinates(), setColors3() and
+ * setTextureCoordinatesParames(1, 2) methods implies a
+ * format of COORDINATES | COLOR_3
+ * | TEXTURE_COORDINATE_2. Indexed representation is specified by calling
+ * the methods that specify the indices, for example
+ * setCoordinateIndices().<p>
+ * Stripped primitives are loaded using the TRIANGLE_FAN_ARRAY or
+ * TRIANGLE_STRIP_ARRAY flags to the constructor. The setStripCounts()
+ * method specifies the length of each strip.<p>
+ * A set of complex polygons is loaded using the POLYGON_ARRAY
+ * flag to the constructor. The setStripCounts() method specifies the length
+ * of each contour of the polygons. The setContourCounts() method specifies
+ * the number of countours in each polygon. For example, a triangle with a
+ * triangular hole would have strip counts [3, 3] (indicating two contours of
+ * three points) and contour counts [2] (indicating a single polygon with two
+ * contours).<p>
+ * GeometryInfo itelf contains some simple utilities, such as
+ * calculating indices for non-indexed data ("indexifying") and getting rid
+ * of unused data in your indexed geometry ("compacting").<p>
+ * The geometry utility tools modify the contents of the
+ * GeometryInfo. After processing, the resulting geometry can be extracted
+ * from the GeometryInfo by calling getGeometryArray(). If multiple tools
+ * are used, the order of processing should be: generate normals, then
+ * stripify. For example, to convert a general mesh of polygons without
+ * normals into an optimized mesh call:
+ * <pre><blockquote>
+ * GeometryInfo gi = new GeometryInfo(GeometryInfo.POLYGON_ARRAY);
+ * // initialize the geometry info here
+ * // generate normals
+ * NormalGenerator ng = new NormalGenerator();
+ * ng.generateNormals(gi);
+ * // stripify
+ * Stripifier st = new Stripifier();
+ * st.stripify(gi);
+ * GeometryArray result = gi.getGeometryArray();
+ * </blockquote></pre>
+ *
+ * @see NormalGenerator
+ * @see Stripifier
+ * @see com.sun.j3d.utils.compression.CompressionStream
+ * @see com.sun.j3d.utils.compression.GeometryCompressor
+ * @see javax.media.j3d.GeometryArray
+ */
+
+public class GeometryInfo {
+
+ /**
+ * Send to the constructor to inform that the data will be arranged so
+ * that each set of three vertices form an independent triangle
+ */
+ public static final int TRIANGLE_ARRAY = 1;
+
+ /**
+ * Send to the constructor to inform that the data will be arranged so
+ * that each set of four vertices form an independent quad
+ */
+ public static final int QUAD_ARRAY = 2;
+
+ /**
+ * Send to the constructor to inform that the data will be arranged so
+ * that the stripCounts array indicates how many vertices to use
+ * for each triangle fan.
+ */
+ public static final int TRIANGLE_FAN_ARRAY = 3;
+
+ /**
+ * Send to the constructor to inform that the data will be arranged so
+ * that the stripCounts array indicates how many vertices to use
+ * for each triangle strip.
+ */
+ public static final int TRIANGLE_STRIP_ARRAY = 4;
+
+ /**
+ * Send to the constructor to inform that the data is arranged as
+ * possibly multi-contour, possible non-planar polygons.
+ * The stripCounts array indicates how many vertices to use
+ * for each contour, and the contourCounts array indicates how many
+ * stripCounts entries to use for each polygon. The first
+ * contour is the bounding polygon, and subsequent contours are
+ * "holes." If contourCounts is left null, the default is
+ * one contour per polygon.
+ */
+ public static final int POLYGON_ARRAY = 5;
+
+ private int prim;
+
+ // 1 Show indexification details
+ private static final int DEBUG = 0;
+
+ private Point3f coordinates[] = null;
+ private Color3f colors3[] = null;
+ private Color4f colors4[] = null;
+ private Vector3f normals[] = null;
+ private Object texCoordSets[][] = null;
+
+ private int coordinateIndices[] = null;
+ private int colorIndices[] = null;
+ private int normalIndices[] = null;
+ private int texCoordIndexSets[][] = null;
+
+ private int[] texCoordSetMap = null;
+ private int texCoordSetCount = 0;
+ private int texCoordDim = 0;
+
+ private int stripCounts[] = null;
+ private int contourCounts[] = null;
+
+ private Triangulator tr = null;
+ private NormalGenerator ng = null;
+
+ private int oldPrim = 0;
+ private int oldStripCounts[] = null;
+
+ private boolean coordOnly = false;
+
+
+
+ /**
+ * Constructor.
+ * Creates an empty GeometryInfo object.
+ * @param primitive Tells the GeometryInfo object the type of
+ * primitive data to be stored
+ * in it, so it will know the format of the data. It can be one of
+ * TRIANGLE_ARRAY,
+ * QUAD_ARRAY, TRIANGLE_FAN_ARRAY, TRIANGLE_STRIP_ARRAY, or POLYGON_ARRAY.
+ */
+ public GeometryInfo(int primitive)
+ {
+ if ((primitive >= TRIANGLE_ARRAY) && (primitive <= POLYGON_ARRAY)) {
+ prim = primitive;
+ } else {
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo0"));
+ }
+ } // End of GeometryInfo(int)
+
+
+
+ /**
+ * Contructor. Populates the GeometryInfo with the geometry from
+ * the GeometryArray.<p>
+ * If the GeometryArray uses the <code>Initial</code> and
+ * <code>Valid</code> GeometryArray methods (<code>
+ * setInitialVertexIndex()</code> and <code>setValidVertexCount()
+ * </code> and their cousins) then only the needed geometry
+ * is copied into the GeometryInfo.
+ */
+ public GeometryInfo(GeometryArray ga)
+ {
+ GeometryInfoGenerator.create(this, ga);
+ } // End of GeometryInfo(GeometryArray)
+
+
+
+ /**
+ * Removes all data from the GeometryInfo and resets the primitive.
+ * After a call to reset(), the GeometryInfo object will be just like
+ * it was when it was newly constructed.
+ * @param primitive Either TRIANGLE_ARRAY, QUAD_ARRAY,
+ * TRIANGLE_FAN_ARRAY, TRIANGLE_STRIP_ARRAY, or POLYGON_ARRAY.
+ * Tells the GeometryInfo object the type of primitive data to be stored
+ * in it, so it will know the format of the data.
+ */
+ public void reset(int primitive)
+ {
+ if ((primitive >= TRIANGLE_ARRAY) && (primitive <= POLYGON_ARRAY)) {
+ prim = primitive;
+ } else {
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo0"));
+ }
+
+ coordinates = null;
+ colors3 = null;
+ colors4 = null;
+ normals = null;
+
+ coordinateIndices = null;
+ colorIndices = null;
+ normalIndices = null;
+
+ stripCounts = null;
+ contourCounts = null;
+
+ oldPrim = 0;
+ oldStripCounts = null;
+
+ texCoordDim = 0;
+ texCoordSetCount = 0;
+ texCoordSets = null;
+ texCoordIndexSets = null;
+ texCoordSetMap = null;
+
+ coordOnly = false;
+
+ } // End of reset(int)
+
+
+
+ /**
+ * Removes all data from this GeometryInfo and populates it with
+ * the geometry from the GeometryArray.
+ */
+ public void reset(GeometryArray ga)
+ {
+ GeometryInfoGenerator.create(this, ga);
+ } // End of reset(GeometryArray)
+
+
+
+ // This method takes an indexed quad array and expands it to
+ // a list of indexed triangles. It is used for the Coordinate
+ // indices as well as the color and texture indices.
+ private int[] expandQuad(int indices[])
+ {
+ int triangles[] = new int[indices.length / 4 * 6];
+
+ for (int i = 0 ; i < indices.length / 4 ; i++ ) {
+ triangles[i * 6 + 0] = indices[i * 4];
+ triangles[i * 6 + 1] = indices[i * 4 + 1];
+ triangles[i * 6 + 2] = indices[i * 4 + 2];
+ triangles[i * 6 + 3] = indices[i * 4];
+ triangles[i * 6 + 4] = indices[i * 4 + 2];
+ triangles[i * 6 + 5] = indices[i * 4 + 3];
+ }
+
+ return triangles;
+ } // End of expandQuad
+
+
+
+ // This method takes an indexed triangle fan and expands it to
+ // a list of indexed triangles. It is used for the Coordinate
+ // indices as well as the color and texture indices.
+ private int[] expandTriFan(int numTris, int indices[])
+ {
+ int triangles[] = new int[numTris * 3];
+ int p = 0;
+ int base = 0;
+ for (int f = 0 ; f < stripCounts.length ; f++) {
+ for (int t = 0 ; t < stripCounts[f] - 2 ; t++) {
+ triangles[p++] = indices[base];
+ triangles[p++] = indices[base + t + 1];
+ triangles[p++] = indices[base + t + 2];
+ }
+ base += stripCounts[f];
+ }
+ return triangles;
+ } // End of expandTriFan
+
+
+
+ // This method takes an indexed triangle strip and expands it to
+ // a list of indexed triangles. It is used for the Coordinate
+ // indices as well as the color and texture indices.
+ private int[] expandTriStrip(int numTris, int indices[])
+ {
+ int triangles[] = new int[numTris * 3];
+
+ int p = 0;
+ int base = 0;
+ for (int s = 0 ; s < stripCounts.length ; s++) {
+ for (int t = 0 ; t < stripCounts[s] - 2 ; t++) {
+
+ // Use a ping-ponging algorithm to reverse order on every other
+ // triangle to preserve winding
+ if (t % 2 == 0) {
+ triangles[p++] = indices[base + t + 0];
+ triangles[p++] = indices[base + t + 1];
+ triangles[p++] = indices[base + t + 2];
+ } else {
+ triangles[p++] = indices[base + t + 0];
+ triangles[p++] = indices[base + t + 2];
+ triangles[p++] = indices[base + t + 1];
+ }
+ }
+ base += stripCounts[s];
+ }
+
+ return triangles;
+ } // End of expandTriStrip
+
+
+
+ // Used by the NormalGenerator utility. Informs the GeometryInfo object
+ // to remember its current primitive and stripCounts arrays so that
+ // they can be used to convert the object back to its original
+ // primitive
+ void rememberOldPrim()
+ {
+ oldPrim = prim;
+ oldStripCounts = stripCounts;
+ } // End of rememberOldPrim
+
+
+
+ // The NormalGenerator needs to know the original primitive for
+ // facet normal generation for quads
+ int getOldPrim()
+ {
+ return oldPrim;
+ } // End of getOldPrim
+
+
+
+ // Used by the Utility libraries other than the NormalGenerator.
+ // Informs the GeometryInfo object that the geometry need not
+ // be converted back to the original primitive before returning.
+ // For example, if a list of Fans is sent, converted to Triangles
+ // for normal generation, and then stripified by the Stripifyer,
+ // we want to make sure that GeometryInfo doesn't convert the
+ // geometry *back* to fans before creating the output GeometryArray.
+ void forgetOldPrim()
+ {
+ oldPrim = 0;
+ oldStripCounts = null;
+ } // End of forgetOldPrim
+
+
+
+ // We have changed the user's data from their original primitive
+ // type to TRIANGLE_ARRAY. If this method is being called, it
+ // means we need to change it back (to try and hide from the user
+ // the fact that we've converted). This usually happens when
+ // the user has used GeometryInfo for generating normals, but
+ // they are not Stripifying or Triangulating. The function is
+ // called from getGeometryArray before creating the output data.
+ private void changeBackToOldPrim()
+ {
+ if (oldPrim != 0) {
+ convertToIndexedTriangles();
+ if (ng == null) ng = new NormalGenerator();
+ ng.convertBackToOldPrim(this, oldPrim, oldStripCounts);
+ oldPrim = 0;
+ oldStripCounts = null;
+ }
+ } // End of changeBackToOldPrim
+
+
+
+ /**
+ * Convert the GeometryInfo object to have primitive type TRIANGLE_ARRAY
+ * and be indexed.
+ * @throws IllegalArgumentException if coordinate data is missing,
+ * if the index lists aren't all the
+ * same length, if an index list is set and the corresponding data
+ * list isn't set, if a data list is set and the corresponding
+ * index list is unset (unless all index lists are unset or in
+ * USE_COORD_INDEX_ONLY format),
+ * if StripCounts or ContourCounts is inconsistent with the current
+ * primitive, if the sum of the contourCounts array doesn't equal
+ * the length of the StripCounts array, or if the number of vertices
+ * isn't a multiple of three (for triangles) or four (for quads).
+ */
+ public void convertToIndexedTriangles()
+ {
+ int triangles = 0;
+
+ // This calls checkForBadData
+ indexify();
+
+ if (prim == TRIANGLE_ARRAY) return;
+
+ switch(prim) {
+
+ case QUAD_ARRAY:
+
+ coordinateIndices = expandQuad(coordinateIndices);
+ if (colorIndices != null) colorIndices = expandQuad(colorIndices);
+ if (normalIndices != null)
+ normalIndices = expandQuad(normalIndices);
+ for (int i = 0 ; i < texCoordSetCount ; i++)
+ texCoordIndexSets[i] = expandQuad(texCoordIndexSets[i]);
+ break;
+
+ case TRIANGLE_FAN_ARRAY:
+ // Count how many triangles are in the object
+ for (int i = 0 ; i < stripCounts.length ; i++) {
+ triangles += stripCounts[i] - 2;
+ }
+
+ coordinateIndices = expandTriFan(triangles, coordinateIndices);
+ if (colorIndices != null)
+ colorIndices = expandTriFan(triangles, colorIndices);
+ if (normalIndices != null)
+ normalIndices = expandTriFan(triangles, normalIndices);
+ for (int i = 0 ; i < texCoordSetCount ; i++)
+ texCoordIndexSets[i] = expandTriFan(triangles,
+ texCoordIndexSets[i]);
+ break;
+
+ case TRIANGLE_STRIP_ARRAY:
+ // Count how many triangles are in the object
+ for (int i = 0 ; i < stripCounts.length ; i++) {
+ triangles += stripCounts[i] - 2;
+ }
+
+ coordinateIndices = expandTriStrip(triangles, coordinateIndices);
+ if (colorIndices != null)
+ colorIndices = expandTriStrip(triangles, colorIndices);
+ if (normalIndices != null)
+ normalIndices = expandTriStrip(triangles, normalIndices);
+ for (int i = 0 ; i < texCoordSetCount ; i++)
+ texCoordIndexSets[i] = expandTriStrip(triangles,
+ texCoordIndexSets[i]);
+ break;
+
+ case POLYGON_ARRAY:
+ if (tr == null) tr = new Triangulator();
+ tr.triangulate(this);
+ break;
+ }
+
+ prim = TRIANGLE_ARRAY;
+ stripCounts = null;
+ } // End of convertToIndexedTriangles
+
+
+
+ /**
+ * Get the current primitive. Some of the utilities may change the
+ * primitive type of the data stored in the GeometryInfo object
+ * (for example, the stripifyer will change it to TRIANGLE_STRIP_ARRAY).
+ */
+ public int getPrimitive()
+ {
+ return prim;
+ } // End of getPrimitive()
+
+
+
+ /**
+ * Set the current primitive. Some of the utilities may change the
+ * primitive type of the data stored in the GeometryInfo object
+ * (for example, the stripifyer will change it to TRIANGLE_STRIP_ARRAY).
+ * But the user can't change the primitive type - it is set in the
+ * constructor. Therefore, this method has package scope.
+ */
+ void setPrimitive(int primitive)
+ {
+ if ((prim >= TRIANGLE_ARRAY) && (prim <= POLYGON_ARRAY)) {
+ prim = primitive;
+ } else {
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo0"));
+ }
+ } // End of setPrimitive()
+
+
+
+ /**
+ * Sets the coordinates array.
+ * No data copying is done because a reference to user data is used.
+ */
+ public void setCoordinates(Point3f coordinates[])
+ {
+ this.coordinates = coordinates;
+ } // End of setCoordinates
+
+
+
+ /**
+ * Sets the coordinates array.
+ * The points are copied into the GeometryInfo object.
+ */
+ public void setCoordinates(Point3d coordinates[])
+ {
+ if (coordinates == null) this.coordinates = null;
+ else {
+ this.coordinates = new Point3f[coordinates.length];
+ for (int i = 0 ; i < coordinates.length ; i++) {
+ this.coordinates[i] = new Point3f(
+ (float)(coordinates[i].x),
+ (float)(coordinates[i].y),
+ (float)(coordinates[i].z));
+ }
+ }
+ } // End of setCoordinates
+
+
+
+ /**
+ * Sets the coordinates array.
+ * The points are copied into the GeometryInfo object.
+ */
+ public void setCoordinates(float coordinates[])
+ {
+ if (coordinates == null) this.coordinates = null;
+ else {
+ this.coordinates = new Point3f[coordinates.length / 3];
+ for (int i = 0 ; i < this.coordinates.length ; i++) {
+ this.coordinates[i] = new Point3f(coordinates[i * 3],
+ coordinates[i * 3 + 1],
+ coordinates[i * 3 + 2]);
+ }
+ }
+ } // End of setCoordinates
+
+
+
+ /**
+ * Sets the coordinates array.
+ * The points are copied into the GeometryInfo object.
+ */
+ public void setCoordinates(double coordinates[])
+ {
+ if (coordinates == null) this.coordinates = null;
+ else {
+ this.coordinates = new Point3f[coordinates.length / 3];
+ for (int i = 0 ; i < coordinates.length / 3 ; i++) {
+ this.coordinates[i] = new Point3f((float)coordinates[i * 3],
+ (float)coordinates[i * 3 + 1],
+ (float)coordinates[i * 3 + 2]);
+ }
+ }
+ } // End of setCoordinates
+
+
+
+ /**
+ * Retrieves a reference to the coordinate array.
+ */
+ public Point3f[] getCoordinates()
+ {
+ return coordinates;
+ } // End of getCoordinates
+
+
+
+ /**
+ * Sets the colors array.
+ * No data copying is done because a reference to
+ * user data is used.
+ */
+ public void setColors(Color3f colors[])
+ {
+ colors3 = colors;
+ colors4 = null;
+ } // End of setColors
+
+
+
+ /**
+ * Sets the colors array.
+ * No data copying is done because a reference to
+ * user data is used.
+ */
+ public void setColors(Color4f colors[])
+ {
+ colors3 = null;
+ colors4 = colors;
+ } // End of setColors
+
+
+
+ /**
+ * Sets the colors array.
+ * The points are copied into the GeometryInfo object.
+ */
+ public void setColors(Color3b colors[])
+ {
+ if (colors == null) {
+ colors3 = null;
+ colors4 = null;
+ } else {
+ colors3 = new Color3f[colors.length];
+ colors4 = null;
+ for (int i = 0 ; i < colors.length ; i++) {
+ colors3[i] = new Color3f((float) (colors[i].x & 0xff) / 255.0f,
+ (float) (colors[i].y & 0xff) / 255.0f,
+ (float) (colors[i].z & 0xff) / 255.0f);
+ }
+ }
+ } // End of setColors
+
+
+
+ /**
+ * Sets the colors array.
+ * The points are copied into the GeometryInfo object.
+ */
+ public void setColors(Color4b colors[])
+ {
+ if (colors == null) {
+ colors3 = null;
+ colors4 = null;
+ } else {
+ colors3 = null;
+ colors4 = new Color4f[colors.length];
+ for (int i = 0 ; i < colors.length ; i++) {
+ colors4[i] = new Color4f((float) (colors[i].x & 0xff) / 255.0f,
+ (float) (colors[i].y & 0xff) / 255.0f,
+ (float) (colors[i].z & 0xff) / 255.0f,
+ (float) (colors[i].w & 0xff) / 255.0f);
+ }
+ }
+ } // End of setColors
+
+
+
+ /**
+ * Sets the colors array.
+ * The points are copied into the GeometryInfo object, assuming
+ * 3 components (R, G, and B) per vertex.
+ */
+ public void setColors3(float colors[])
+ {
+ if (colors == null) {
+ colors3 = null;
+ colors4 = null;
+ } else {
+ colors3 = new Color3f[colors.length / 3];
+ colors4 = null;
+ for (int i = 0 ; i < colors.length / 3 ; i++) {
+ colors3[i] = new Color3f(colors[i * 3],
+ colors[i * 3 + 1],
+ colors[i * 3 + 2]);
+ }
+ }
+ } // End of setColors3
+
+
+
+ /**
+ * Sets the colors array.
+ * The points are copied into the GeometryInfo object, assuming
+ * 4 components (R, G, B, and A) per vertex.
+ */
+ public void setColors4(float colors[])
+ {
+ if (colors == null) {
+ colors3 = null;
+ colors4 = null;
+ } else {
+ colors3 = null;
+ colors4 = new Color4f[colors.length / 4];
+ for (int i = 0 ; i < colors.length / 4 ; i++) {
+ colors4[i] = new Color4f(colors[i * 4],
+ colors[i * 4 + 1],
+ colors[i * 4 + 2],
+ colors[i * 4 + 3]);
+ }
+ }
+ } // End of setColors4
+
+
+
+ /**
+ * Sets the colors array.
+ * The points are copied into the GeometryInfo object, assuming
+ * 3 components (R, G, and B) per vertex.
+ */
+ public void setColors3(byte colors[])
+ {
+ if (colors == null) {
+ colors3 = null;
+ colors4 = null;
+ } else {
+ colors3 = new Color3f[colors.length / 3];
+ colors4 = null;
+ for (int i = 0 ; i < colors.length / 3 ; i++) {
+ colors3[i] =
+ new Color3f((float)(colors[i * 3] & 0xff) / 255.0f,
+ (float)(colors[i * 3 + 1] & 0xff) / 255.0f,
+ (float)(colors[i * 3 + 2] & 0xff) / 255.0f);
+ }
+ }
+ } // End of setColors3
+
+
+
+ /**
+ * Sets the colors array.
+ * The points are copied into the GeometryInfo object, assuming
+ * 4 components (R, G, B, and A) per vertex.
+ */
+ public void setColors4(byte colors[])
+ {
+ if (colors == null) {
+ colors3 = null;
+ colors4 = null;
+ } else {
+ colors3 = null;
+ colors4 = new Color4f[colors.length / 4];
+ for (int i = 0 ; i < colors.length / 4 ; i++) {
+ colors4[i] =
+ new Color4f((float)(colors[i * 4] & 0xff) / 255.0f,
+ (float)(colors[i * 4 + 1] & 0xff) / 255.0f,
+ (float)(colors[i * 4 + 2] & 0xff) / 255.0f,
+ (float)(colors[i * 4 + 3] & 0xff) / 255.0f);
+ }
+ }
+ } // End of setColors4
+
+
+
+ /**
+ * Retrieves a reference to the colors array. Will be either
+ * <code>Color3f[]</code> or <code>Color4f[]</code> depending on
+ * the type of the input data. Call
+ * getNumColorComponents() to find out which version is returned.
+ */
+ public Object[] getColors()
+ {
+ if (colors3 != null) return colors3;
+ else return colors4;
+ } // End of getColors
+
+
+
+ /**
+ * Returns the number of color data components stored per vertex
+ * in the current GeometryInfo object (3 for RGB or 4 for RGBA).
+ * If no colors are currently defined, 0 is returned.
+ */
+ public int getNumColorComponents()
+ {
+ if (colors3 != null) return 3;
+ else if (colors4 != null) return 4;
+ else return 0;
+ } // End of getNumColorComponents
+
+
+
+ /**
+ * Sets the normals array.
+ * No data copying is done because a reference to
+ * user data is used.
+ */
+ public void setNormals(Vector3f normals[])
+ {
+ this.normals = normals;
+ } // End of setNormals
+
+
+
+ /**
+ * Sets the normals array.
+ * The points are copied into the GeometryInfo object.
+ */
+ public void setNormals(float normals[])
+ {
+ if (normals == null) this.normals = null;
+ else {
+ this.normals = new Vector3f[normals.length / 3];
+ for (int i = 0 ; i < this.normals.length ; i++) {
+ this.normals[i] = new Vector3f(normals[i * 3],
+ normals[i * 3 + 1],
+ normals[i * 3 + 2]);
+ }
+ }
+ } // End of setNormals(float[])
+
+
+
+ /**
+ * Retrieves a reference to the normal array.
+ */
+ public Vector3f[] getNormals()
+ {
+ return normals;
+ } // End of getNormals
+
+
+
+ /**
+ * This method is used to specify the number of texture coordinate sets
+ * and the dimensionality of the texture coordinates.
+ * The number of texture coordinate sets must be specified to the GeometryInfo
+ * class before any of the sets are specified. The dimensionality of the
+ * texture coordinates may be 2, 3, or 4, corresponding to 2D, 3D, or 4D
+ * texture coordinates respectively.(All sets must have the same
+ * dimensionality.) The default is zero, 2D texture coordinate sets.
+ * This method should be called before any texture coordinate sets are
+ * specified because <b>calling this method will delete all previously
+ * specified texture coordinate and texture coordinate index arrays</b>
+ * associated with this GeometryInfo. For example:
+ * <blockquote><pre>
+ * geomInfo.setTextureCoordinateParams(2, 3);
+ * geomInfo.setTextureCoordinates(0, tex0);
+ * geomInfo.setTextureCoordinates(1, tex1);
+ * geomInfo.setTextureCoordinateParams(1, 2);
+ * geomInfo.getTexCoordSetCount();
+ * </blockquote></pre>
+ * The second call to <code>setTextureCoordinateParams</code> will erase all
+ * the texture coordinate arrays, so the subsequent call to <code>
+ * getTexCoordSetCount</code> will return 1.
+ * @param numSets The number of texture coordinate sets that will be
+ * specified for this GeometryInfo object.
+ * @param dim The dimensionality of the texture coordinates. Has to be 2, 3
+ * or 4.
+ * @throws IllegalArgumentException if the dimensionality of the texture
+ * coordinates is not one of 2, 3 or 4.
+ */
+ public void setTextureCoordinateParams(int numSets, int dim)
+ {
+ if (dim == 2) {
+ texCoordSets = new TexCoord2f[numSets][];
+ } else if (dim == 3) {
+ texCoordSets = new TexCoord3f[numSets][];
+ } else if (dim == 4) {
+ texCoordSets = new TexCoord4f[numSets][];
+ } else {
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo9"));
+ }
+ texCoordIndexSets = new int[numSets][];
+ texCoordDim = dim;
+ texCoordSetCount = numSets;
+ } // End of setTextureCoordinateParams
+
+
+
+ /**
+ * Returns the number of texture coordinate sets in this GeometryInfo.
+ * This value is set with setTextureCoordinateParams().
+ * If setTextureCoordinateParams()
+ * has not been called, 0 is returned unless one of the deprecated
+ * texture coordinate methods has been called. Calling one of the
+ * deprecated texture coordinate methods sets the count to 1.
+ * The deprecated texture coordinate methods are those that don't
+ * take texCoordSet as the first parameter.
+ * @return the number of texture coordinate sets in this
+ * GeometryInfo.
+ */
+ public int getTexCoordSetCount() {
+ return texCoordSetCount;
+ }
+
+
+
+ /**
+ * Returns the number of texture coordinate components that are stored
+ * per vertex. Returns 2 for ST (2D), 3 for STR (3D),
+ * or 4 for STRQ (4D), aslo known as the "dimensionality" of the
+ * coordinates. This value is set with
+ * setTextureCoordinateParams(). If setTextureCoordinateParams()
+ * has not been called, 0 is returned unless one of the deprecated
+ * texture coordinate methods has been called. Calling one of the
+ * deprecated texture coordinate methods sets the dimensionality
+ * explicitly (if you called setTextureCoordinates(Point2f[]) then
+ * 2 is returned).
+ * The deprecated texture coordinate methods are those that don't
+ * take texCoordSet as the first parameter.
+ */
+ public int getNumTexCoordComponents()
+ {
+ return texCoordDim;
+ } // End of getNumTexCoordComponents
+
+
+
+ /**
+ * Sets the mapping between texture coordinate sets and texture units.
+ * See the
+ * <a href="../../../../../javax/media/j3d/GeometryArray.html#texCoordSetMap">
+ * GeometryArray constructor </a> for further details.
+ * <p> <b>Note:</b> If the texCoordSetMap is not set, multi-texturing is
+ * turned off. Only the texture coordinate set at index 0 (if set) will be
+ * used. Any other sets specified by the GeometryInfo.setTextureCoordinate*
+ * methods will be ignored.
+ */
+ public void setTexCoordSetMap(int map[]) {
+ texCoordSetMap = map;
+ }
+
+
+
+ /**
+ * Returns a reference to the texture coordinate set map.
+ * See the
+ * <a href="../../../../../javax/media/j3d/GeometryArray.html#texCoordSetMap">
+ * GeometryArray constructor </a> for further details.
+ */
+ public int[] getTexCoordSetMap() {
+ return texCoordSetMap;
+ }
+
+
+
+ /**
+ * Sets the 2D texture coordinates for the specified set.
+ * No data copying is done - a reference to user data is used.
+ * @param texCoordSet The texture coordinate set for which these
+ * coordinates are being specified.
+ * @param texCoords Array of 2D texture coordinates.
+ * @throws IllegalArgumentException if <code>texCoordSet </code> < 0 or
+ * <code>texCoordSet >= texCoordSetCount</code>,
+ * or the texture coordinate parameters were not previously set by
+ * calling <code>setTextureCoordinateParams(texCoordSetCount, 2)</code>.
+ */
+ public void setTextureCoordinates(int texCoordSet, TexCoord2f texCoords[])
+ {
+ if (texCoordDim != 2)
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo15"));
+ if ((texCoordSet >= texCoordSetCount) || (texCoordSet < 0))
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo18"));
+
+ texCoordSets[texCoordSet] = texCoords;
+ } // End of setTextureCoordinates(int, TexCoord3f[])
+
+
+
+ /**
+ * Sets the TextureCoordinates array by copying the data
+ * into the GeometryInfo object.
+ * This method sets the number of texture coordinate sets to 1,
+ * sets the dimensionality of the texture coordinates to 2,
+ * and sets the coordinates for texture coordinate set 0.
+ * @deprecated As of Java 3D 1.3 replaced by
+ * <code>setTextureCoordinates(int texCoordSet, TexCoord2f coords[])</code>
+ */
+ public void setTextureCoordinates(Point2f texCoords[])
+ {
+ texCoordSetCount = 1;
+ texCoordDim = 2;
+ texCoordSets = new TexCoord2f[1][];
+ if (texCoords != null) {
+ TexCoord2f[] tex = new TexCoord2f[texCoords.length];
+ for (int i = 0 ; i < texCoords.length ; i++)
+ tex[i] = new TexCoord2f(texCoords[i]);
+ texCoordSets[0] = tex;
+ }
+ } // End of setTextureCoordinates(Point2f[])
+
+
+
+ /**
+ * Sets the texture coordinates array for the specified set.
+ * No data copying is done - a reference to user data is used.
+ * @param texCoordSet The texture coordinate set for which these coordinates
+ * are being specified.
+ * @param texCoords Array of 3D texture coordinates.
+ * @throws IllegalArgumentException if <code> texCoordSet </code> < 0 or
+ * <code>texCoordSet >= texCoordSetCount</code>,
+ * or the texture coordinate parameters were not previously set by
+ * calling <code>setTextureCoordinateParams(texCoordSetCount, 3)</code>.
+ */
+ public void setTextureCoordinates(int texCoordSet, TexCoord3f texCoords[])
+ {
+ if (texCoordDim != 3)
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo16"));
+ if ((texCoordSet >= texCoordSetCount) || (texCoordSet < 0))
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo18"));
+
+ texCoordSets[texCoordSet] = texCoords;
+ } // End of setTextureCoordinates(int, TexCoord3f[])
+
+
+
+ /**
+ * Sets the TextureCoordinates array by copying the data
+ * into the GeometryInfo object.
+ * This method sets the number of texture coordinate sets to 1,
+ * sets the dimensionality of the texture coordinates to 3,
+ * and sets the coordinates for texture coordinate set 0.
+ * @deprecated As of Java 3D 1.3 replaced by
+ * <code>setTextureCoordinates(int texCoordSet, TexCoord3f coords[])</code>
+ */
+ public void setTextureCoordinates(Point3f texCoords[])
+ {
+ texCoordSetCount = 1;
+ texCoordDim = 3;
+ texCoordSets = new TexCoord3f[1][];
+ if (texCoords != null) {
+ TexCoord3f[] tex = new TexCoord3f[texCoords.length];
+ for (int i = 0 ; i < texCoords.length ; i++)
+ tex[i] = new TexCoord3f(texCoords[i]);
+ texCoordSets[0] = tex;
+ }
+ } // End of setTextureCoordinates(Point3f[])
+
+
+
+ /**
+ * Sets the texture coordinates array for the specified set.
+ * No data copying is done - a reference to user data is used.
+ * @param texCoordSet The texture coordinate set for which these coordinates
+ * are being specified.
+ * @param texCoords Array of 4D texture coordinates.
+ * @throws IllegalArgumentException if <code> texCoordSet </code> < 0 or
+ * <code>texCoordSet >= texCoordSetCount</code>,
+ * or the texture coordinate parameters were not previously set by
+ * calling <code>setTextureCoordinateParams(texCoordSetCount, 4)</code>.
+ */
+ public void setTextureCoordinates(int texCoordSet, TexCoord4f texCoords[]) {
+ if (texCoordDim != 4)
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo17"));
+ if ((texCoordSet >= texCoordSetCount) || (texCoordSet < 0))
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo18"));
+
+ texCoordSets[texCoordSet] = texCoords;
+ } // End of setTextureCoordinates(int, TexCoord4f[])
+
+
+
+ /**
+ * Sets the texture coordinates array by copying the data into the
+ * GeometryInfo object. The number of sets and dimensionality of
+ * the sets must have been set previously with
+ * setTextureCoordinateParams(texCoordSetCount, dim).
+ * @param texCoordSet The texture coordinate set for which these coordinates
+ * are being specified.
+ * @param texCoords The float array of texture coordinates. For n texture
+ * coordinates with dimensionality d, there must be d*n floats in the array.
+ * @throws IllegalArgumentException if <code>texCoordSet </code> < 0 or
+ * <code>texCoordSet >= texCoordSetCount</code>,
+ * or the texture coordinate parameters were not previously set by
+ * calling <code>setTextureCoordinateParams</code>.
+ */
+ public void setTextureCoordinates(int texCoordSet, float texCoords[])
+ {
+ if ((texCoords.length % texCoordDim) != 0)
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo2"));
+
+ // Copy the texCoords into this GeometryInfo object
+ if (texCoordDim == 2) {
+ TexCoord2f tcoords[] = new TexCoord2f[texCoords.length / 2];
+ for (int i = 0 ; i < tcoords.length ; i++)
+ tcoords[i] = new TexCoord2f(texCoords[i * 2],
+ texCoords[i * 2 + 1]);
+ setTextureCoordinates(texCoordSet, tcoords);
+ } else if (texCoordDim == 3) {
+ TexCoord3f tcoords[] = new TexCoord3f[texCoords.length / 3];
+ for (int i = 0 ; i < tcoords.length ; i++)
+ tcoords[i] = new TexCoord3f(texCoords[i * 3],
+ texCoords[i * 3 + 1],
+ texCoords[i * 3 + 2]);
+ setTextureCoordinates(texCoordSet, tcoords);
+ } else if (texCoordDim == 4) {
+ TexCoord4f tcoords[] = new TexCoord4f[texCoords.length / 4];
+ for (int i = 0 ; i < tcoords.length ; i++)
+ tcoords[i] = new TexCoord4f(texCoords[i * 4],
+ texCoords[i * 4 + 1],
+ texCoords[i * 4 + 2],
+ texCoords[i * 4 + 3]);
+ setTextureCoordinates(texCoordSet, tcoords);
+ } else {
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo21"));
+ }
+ } // End of setTextureCoordinates(int, float[])
+
+
+
+ /**
+ * Sets the texture coordinates array by copying the data
+ * into the GeometryInfo object, assuming two numbers
+ * (S and T) per vertex.
+ * This method sets the number of texture coordinate sets to 1,
+ * sets the dimensionality of the texture coordinates to 2,
+ * and sets the coordinates for texture coordinate set 0.
+ * @deprecated As of Java 3D 1.3 replaced by
+ * <code>setTextureCoordinates(int texCoordSet, float texCoords[])</code>
+ */
+ public void setTextureCoordinates2(float texCoords[])
+ {
+ texCoordSetCount = 1;
+ texCoordDim = 2;
+ texCoordSets = new TexCoord2f[1][];
+ setTextureCoordinates(0, texCoords);
+ } // End of setTextureCoordinates2(float[])
+
+
+
+ /**
+ * Sets the TextureCoordinates array by copying the data
+ * into the GeometryInfo object, assuming three numbers
+ * (S, T, &amp; R) per vertex.
+ * This method sets the number of texture coordinate sets to 1,
+ * sets the dimensionality of the texture coordinates to 3,
+ * and sets the coordinates for texture coordinate set 0.
+ * @deprecated As of Java 3D 1.3 replaced by
+ * <code>setTextureCoordinates(int texCoordSet, float texCoords[])</code>
+ */
+ public void setTextureCoordinates3(float texCoords[])
+ {
+ texCoordSetCount = 1;
+ texCoordDim = 3;
+ texCoordSets = new TexCoord3f[1][];
+ setTextureCoordinates(0, texCoords);
+ } // End of setTextureCoordinates3(float[])
+
+
+
+ /**
+ * Returns a reference to the indicated texture coordinate array.
+ * The return type will be <code>TexCoord2f[]</code>, <code>TexCoord3f[]
+ * </code>, or <code>TexCoord4f[]</code> depending on the
+ * current dimensionality of the texture coordinates in the GeometryInfo
+ * object. Use <code>getNumTexCoordComponents()</code> to find out which
+ * version is returned.
+ * @param texCoordSet The index of the texture coordinate set to
+ * retrieve.
+ * @return An array of texture coordinates at the specified index
+ * @throws IllegalArgumentException If <code> texCoordSet</code> < 0
+ * or <code>texCoordSet >= texCoordSetCount</code>
+ */
+ public Object[] getTextureCoordinates(int texCoordSet)
+ {
+ if ((texCoordSet >= texCoordSetCount) || (texCoordSet < 0))
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo18"));
+ return texCoordSets[texCoordSet];
+ } // End of getTextureCoordinates(int)
+
+
+
+ /**
+ * Retrieves a reference to texture coordinate set 0.
+ * The return type will be <code>TexCoord2f[]</code>, <code>TexCoord3f[]
+ * </code>, or <code>TexCoord4f[]</code> depending on the
+ * current dimensionality of the texture coordinates in the GeometryInfo
+ * object. Use <code>getNumTexCoordComponents()</code> to find out which
+ * version is returned. Equivalent to <code>getTextureCoordinates(0)</code>.
+ * @return An array of texture coordinates for set 0.
+ * @deprecated As of Java 3D 1.3 replaced by
+ * <code>getTextureCoordinates(int texCoordSet)</code>
+ */
+ public Object[] getTextureCoordinates()
+ {
+ return texCoordSets[0];
+ } // End of getTextureCoordinates()
+
+
+
+ /**
+ * Sets the array of indices into the Coordinate array.
+ * No data copying is done - a reference to user data is used.
+ */
+ public void setCoordinateIndices(int coordinateIndices[])
+ {
+ this.coordinateIndices = coordinateIndices;
+ } // End of setCoordinateIndices
+
+
+
+ /**
+ * Retrieves a reference to the array of indices into the
+ * coordinate array.</p>
+ *
+ * This method should be considered for advanced users only.
+ * Novice users should just use getGeometryArray() to retrieve
+ * their data so that the internal format of GeometryInfo is
+ * of no concern.</p>
+ *
+ * Depending on which of the utility routines you've called
+ * on your GeometryInfo object, the results may not be what you
+ * expect. If you've called the Stripifier, your GeometryInfo
+ * object's Primitive has been changed to indexed TRIANGLE_STRIP_ARRAY
+ * and your data will be formatted accordingly. Similarly, if
+ * you've called the Triangulator, your data is in indexed
+ * TRIANGLE_ARRAY format. Generating normals with the NormalGenerator
+ * utility will convert your data to indexed TRIANGLE_ARRAY also,
+ * but if you call getGeometryArray without calling the Stripifier or
+ * Triangulator, your data will be converted back to the original
+ * primitive type when creating the GeometryArray object to pass
+ * back. However, if your creaseAngle was not Math.PI (no creases -
+ * smooth shading), then the introduction of
+ * creases into your model may have split primitives, lengthening
+ * the StripCounts and index arrays from your original data.
+ */
+ public int[] getCoordinateIndices()
+ {
+ return coordinateIndices;
+ } // End of getCoordinateIndices
+
+
+
+ /**
+ * Sets the array of indices into the Color array.
+ * No data copying is done - a reference to user data is used.
+ */
+ public void setColorIndices(int colorIndices[])
+ {
+ this.colorIndices = colorIndices;
+ } // End of setColorIndices
+
+
+
+ /**
+ * Retrieves a reference to the array of indices into the
+ * color array.</p>
+ *
+ * This method should be considered for advanced users only.
+ * Novice users should just use getGeometryArray() to retrieve
+ * their data so that the internal format of GeometryInfo is
+ * of no concern.</p>
+ *
+ * Depending on which of the utility routines you've called
+ * on your GeometryInfo object, the results may not be what you
+ * expect. If you've called the Stripifier, your GeometryInfo
+ * object's Primitive has been changed to indexed TRIANGLE_STRIP_ARRAY
+ * and your data will be formatted accordingly. Similarly, if
+ * you've called the Triangulator, your data is in indexed
+ * TRIANGLE_ARRAY format. Generating normals with the NormalGenerator
+ * utility will convert your data to indexed TRIANGLE_ARRAY also,
+ * but if you call getGeometryArray without calling the Stripifier or
+ * Triangulator, your data will be converted back to the original
+ * primitive type when creating the GeometryArray object to pass
+ * back. However, if your creaseAngle was not Math.PI (no creases -
+ * smooth shading), then the introduction of
+ * creases into your model may have split primitives, lengthening
+ * the StripCounts and index arrays from your original data.
+ */
+ public int[] getColorIndices()
+ {
+ return colorIndices;
+ } // End of getColorIndices
+
+
+
+ /**
+ * Sets the array of indices into the Normal array.
+ * No data copying is done - a reference to user data is used.
+ */
+ public void setNormalIndices(int normalIndices[])
+ {
+ this.normalIndices = normalIndices;
+
+ } // End of setNormalIndices
+
+
+
+ /**
+ * Retrieves a reference to the array of indices into the
+ * Normal array.</p>
+ *
+ * This method should be considered for advanced users only.
+ * Novice users should just use getGeometryArray() to retrieve
+ * their data so that the internal format of GeometryInfo is
+ * of no concern.</p>
+ *
+ * Depending on which of the utility routines you've called
+ * on your GeometryInfo object, the results may not be what you
+ * expect. If you've called the Stripifier, your GeometryInfo
+ * object's Primitive has been changed to indexed TRIANGLE_STRIP_ARRAY
+ * and your data will be formatted accordingly. Similarly, if
+ * you've called the Triangulator, your data is in indexed
+ * TRIANGLE_ARRAY format. Generating normals with the NormalGenerator
+ * utility will convert your data to indexed TRIANGLE_ARRAY also,
+ * but if you call getGeometryArray without calling the Stripifier or
+ * Triangulator, your data will be converted back to the original
+ * primitive type when creating the GeometryArray object to pass
+ * back. However, if your creaseAngle was not Math.PI (no creases -
+ * smooth shading), then the introduction of
+ * creases into your model may have split primitives, lengthening
+ * the StripCounts and index arrays from your original data.
+ */
+ public int[] getNormalIndices()
+ {
+ return normalIndices;
+ } // End of getNormalIndices
+
+
+
+ /**
+ * Sets one of the texture coordinate index arrays.
+ * No data copying is done - a reference to user data is used.
+ * @param texCoordSet The texture coordinate set for which these coordinate
+ * indices are being specified.
+ * @param texIndices The integer array of indices into the specified texture
+ * coordinate set
+ * @throws IllegalArgumentException If <code> texCoordSet</code> < 0 or
+ * <code>texCoordSet >= texCoordSetCount</code>.
+ */
+ public void setTextureCoordinateIndices(int texCoordSet, int texIndices[])
+ {
+ if ((texCoordSet >= texCoordSetCount) || (texCoordSet < 0))
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo18"));
+
+ // Texture coordinates are indexed
+ texCoordIndexSets[texCoordSet] = texIndices;
+ } // End of setTextureCoordinateIndices(int, int[])
+
+
+
+ /**
+ * Sets the array of indices into texture coordinate set 0. Do not
+ * call this method if you are using more than one set of texture
+ * coordinates.
+ * No data is copied - a reference to the user data is used.
+ * @deprecated As of Java 3D 1.3 replaced by
+ * <code>setTextureCoordinateIndices(int texCoordSet, int indices[])</code>
+ * @throws IllegalArgumentException If <code>texCoordSetCount > 1</code>.
+ */
+ public void setTextureCoordinateIndices(int texIndices[])
+ {
+ if (texCoordSetCount > 1)
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo1"));
+ texCoordIndexSets = new int[1][];
+ texCoordIndexSets[0] = texIndices;
+ } // End of setTextureCoordinateIndices(int[])
+
+
+
+ /**
+ * Retrieves a reference to the specified array of texture
+ * coordinate indices.<p>
+ *
+ * This method should be considered for advanced users only.
+ * Novice users should just use getGeometryArray() to retrieve
+ * their data so that the internal format of GeometryInfo is
+ * of no concern.</p>
+ *
+ * Depending on which of the utility routines you've called
+ * on your GeometryInfo object, the results may not be what you
+ * expect. If you've called the Stripifier, your GeometryInfo
+ * object's Primitive has been changed to indexed TRIANGLE_STRIP_ARRAY
+ * and your data will be formatted accordingly. Similarly, if
+ * you've called the Triangulator, your data is in indexed
+ * TRIANGLE_ARRAY format. Generating normals with the NormalGenerator
+ * utility will convert your data to indexed TRIANGLE_ARRAY also,
+ * but if you call getGeometryArray without calling the Stripifier or
+ * Triangulator, your data will be converted back to the original
+ * primitive type when creating the GeometryArray object to pass
+ * back. However, if your creaseAngle was not Math.PI (no creases -
+ * smooth shading), then the introduction of
+ * creases into your model may have split primitives, lengthening
+ * the StripCounts and index arrays from your original data.
+ * @param texCoordSet The texture coordinate index set to be
+ * retrieved.
+ * @return Integer array of the texture coordinate indices for the specified
+ * set.
+ */
+ public int[] getTextureCoordinateIndices(int texCoordSet) {
+ return texCoordIndexSets[texCoordSet];
+ }
+
+
+ /**
+ * Returns a reference to texture coordinate index set 0.
+ * Equivalent to
+ * <code>getTextureCoordinateIndices(0)</code>.
+ * @deprecated As of Java 3D 1.3 replaced by
+ * <code>int[] getTextureCoordinateIndices(int texCoordSet) </code>
+ * @return Integer array of the texture coordinate indices for set 0
+ */
+ public int[] getTextureCoordinateIndices()
+ {
+ if (texCoordIndexSets == null) return null;
+ return texCoordIndexSets[0];
+ } // End of getTextureCoordinateIndices()
+
+
+
+ /**
+ * Sets the array of strip counts. If index lists have been set for
+ * this GeomteryInfo object then the data is indexed and the stripCounts
+ * are like stripIndexCounts. If no index lists have been set then
+ * the data is non-indexed and the stripCounts are like
+ * stripVertexCounts.
+ * @see GeometryStripArray#GeometryStripArray(int, int,
+ * int[] stripVertexCounts)
+ * @see IndexedGeometryStripArray#IndexedGeometryStripArray(int, int, int,
+ * int[] stripIndexCounts)
+ */
+ public void setStripCounts(int stripCounts[])
+ {
+ this.stripCounts = stripCounts;
+ } // End of setStripCounts
+
+
+
+ /**
+ * Retrieves a reference to the array of stripCounts.</p>
+ *
+ * This method should be considered for advanced users only.
+ * Novice users should just use getGeometryArray() to retrieve
+ * their data so that the internal format of GeometryInfo is
+ * of no concern.</p>
+ *
+ * Depending on which of the utility routines you've called
+ * on your GeometryInfo object, the results may not be what you
+ * expect. If you've called the Stripifier, your GeometryInfo
+ * object's Primitive has been changed to indexed TRIANGLE_STRIP_ARRAY
+ * and your data will be formatted accordingly. Similarly, if
+ * you've called the Triangulator, your data is in indexed
+ * TRIANGLE_ARRAY format. Generating normals with the NormalGenerator
+ * utility will convert your data to indexed TRIANGLE_ARRAY also,
+ * but if you call getGeometryArray without calling the Stripifier or
+ * Triangulator, your data will be converted back to the original
+ * primitive type when creating the GeometryArray object to pass
+ * back. However, if your creaseAngle was not Math.PI (no creases -
+ * smooth shading), then the introduction of
+ * creases into your model may have split primitives, lengthening
+ * the StripCounts and index arrays from your original data.
+ */
+ public int[] getStripCounts()
+ {
+ return stripCounts;
+ } // End of getStripCounts
+
+
+
+ /**
+ * Sets the list of contour counts. Only used with the POLYGON_ARRAY
+ * primitive. Polygons can be made of several vertex lists
+ * called contours. The first list is the polygon, and
+ * subsequent lists are "holes" that are removed from the
+ * polygon. All of the holes must be contained entirely
+ * within the polygon.
+ */
+ public void setContourCounts(int contourCounts[])
+ {
+ this.contourCounts = contourCounts;
+ } // End of setContourCounts
+
+
+
+ /**
+ * Retrieves a reference to the array of contourCounts.
+ */
+ public int[] getContourCounts()
+ {
+ return contourCounts;
+ } // End of getContourCounts
+
+
+
+ /*
+ * This routine will return an index list for any array of objects.
+ */
+ int[] getListIndices(Object list[])
+ {
+ // Create list of indices to return
+ int indices[] = new int[list.length];
+
+ // Create hash table with initial capacity equal to the number
+ // of components (assuming about half will be duplicates)
+ HashMap table = new HashMap(list.length);
+
+ Integer idx;
+ for (int i = 0 ; i < list.length ; i++) {
+
+ // Find index associated with this object
+ idx = (Integer)table.get(list[i]);
+
+ if (idx == null) {
+ // We haven't seen this object before
+ indices[i] = i;
+
+ // Put into hash table and remember the index
+ table.put(list[i], new Integer(i));
+
+ } else {
+ // We've seen this object
+ indices[i] = idx.intValue();
+ }
+ }
+
+ return indices;
+ } // End of getListIndices
+
+
+
+ // Class to hash 'size' integers
+ private class IndexRow {
+ int[] val;
+ int size;
+ private static final int HASHCONST = 0xBABEFACE;
+
+ public int hashCode()
+ {
+ int bits = 0;
+ for (int i = 0 ; i < size ; i++) {
+ bits ^= (bits * HASHCONST) << 2;
+ }
+ return bits;
+ } // End of IndexRow.hashCode
+
+ public boolean equals(Object obj)
+ {
+ for (int i = 0 ; i < size ; i++) {
+ if (((IndexRow)obj).get(i) != val[i]) return false;
+ }
+ return true;
+ } // End of IndexRow.equals()
+
+ public int get(int index)
+ {
+ return val[index];
+ } // End of IndexRow.get
+
+ public void set(int index, int value)
+ {
+ val[index] = value;
+ } // End of IndexRow.set
+
+ IndexRow(int numColumns)
+ {
+ size = numColumns;
+ val = new int[size];
+ } // End of IndexRow constructor
+ } // End of class IndexRow
+
+
+
+ /**
+ * Create index lists for all data lists.
+ * Identical data entries are guaranteed to
+ * use the same index value. Does not remove unused data values
+ * from the object - call compact() to do this.
+ * @param useCoordIndexOnly Reformat the data into the
+ * GeometryArray.USE_COORD_INDEX_ONLY format where there is only
+ * one index list. If the data is already in the USE_COORD_INDEX_ONLY
+ * format, sending false (or calling indexify()) will change
+ * it to the normal indexed format.
+ * @throws IllegalArgumentException if coordinate data is missing,
+ * if the index lists aren't all the
+ * same length, if an index list is set and the corresponding data
+ * list isn't set, if a data list is set and the corresponding
+ * index list is unset (unless all index lists are unset or in
+ * USE_COORD_INDEX_ONLY format),
+ * if StripCounts or ContourCounts is inconsistent with the current
+ * primitive, if the sum of the contourCounts array doesn't equal
+ * the length of the StripCounts array, or if the number of vertices
+ * isn't a multiple of three (for triangles) or four (for quads).
+ */
+ public void indexify(boolean useCoordIndexOnly)
+ {
+ checkForBadData();
+
+ if (useCoordIndexOnly) {
+ // Return if already in this format
+ if (coordOnly) return;
+
+ // Start from normal indexed format
+ indexify(false);
+
+ // Reformat data to USE_COORD_INDEX_ONLY format
+ // Need to make an index into the index lists using each
+ // row of indexes as one value
+
+ // First, find out how many index lists there are;
+ int numLists = 1; // Always have coordinates
+ if (colorIndices != null) numLists++;
+ if (normalIndices != null) numLists++;
+ numLists += texCoordSetCount;
+
+ // Make single array containing all indices
+ int n = coordinateIndices.length;
+ IndexRow[] ir = new IndexRow[n];
+ int j;
+ for (int i = 0 ; i < n ; i++) {
+ ir[i] = new IndexRow(numLists);
+ j = 0;
+ ir[i].set(j++, coordinateIndices[i]);
+ if (colorIndices != null) ir[i].set(j++, colorIndices[i]);
+ if (normalIndices != null) ir[i].set(j++, normalIndices[i]);
+ for (int k = 0 ; k < texCoordSetCount ; k++) {
+ ir[i].set(j++, texCoordIndexSets[k][i]);
+ }
+ }
+
+ // Get index into that array
+ int[] coordOnlyIndices = getListIndices(ir);
+
+ // Get rid of duplicate rows
+ int newInd[] = new int[coordOnlyIndices.length];
+ ir = (IndexRow[])compactData(coordOnlyIndices, ir, newInd);
+ coordOnlyIndices = newInd;
+
+ // Reformat data lists to correspond to new index
+
+ // Allocate arrays to hold reformatted data
+ Point3f[] newCoords = new Point3f[ir.length];
+ Color3f[] newColors3 = null;
+ Color4f[] newColors4 = null;
+ Vector3f[] newNormals = null;
+ Object newTexCoordSets[][] = null;
+ if (colors3 != null) newColors3 = new Color3f[ir.length];
+ else if (colors4 != null) newColors4 = new Color4f[ir.length];
+ if (normals != null) newNormals = new Vector3f[ir.length];
+ for (int i = 0 ; i < texCoordSetCount ; i++) {
+ if (texCoordDim == 2) {
+ if (i == 0) newTexCoordSets = new TexCoord2f[texCoordSetCount][];
+ newTexCoordSets[i] = new TexCoord2f[ir.length];
+ } else if (texCoordDim == 3) {
+ if (i == 0) newTexCoordSets = new TexCoord3f[texCoordSetCount][];
+ newTexCoordSets[i] = new TexCoord3f[ir.length];
+ } else if (texCoordDim == 4) {
+ if (i == 0) newTexCoordSets = new TexCoord4f[texCoordSetCount][];
+ newTexCoordSets[i] = new TexCoord4f[ir.length];
+ }
+ }
+
+ // Copy data into new arrays
+ n = ir.length;
+ for (int i = 0 ; i < n ; i++) {
+ j = 0;
+ newCoords[i] = coordinates[(ir[i]).get(j++)];
+ if (colors3 != null) {
+ newColors3[i] = colors3[(ir[i]).get(j++)];
+ } else if (colors4 != null) {
+ newColors4[i] = colors4[(ir[i]).get(j++)];
+ }
+ if (normals != null) newNormals[i] = normals[(ir[i]).get(j++)];
+ for (int k = 0 ; k < texCoordSetCount ; k++) {
+ newTexCoordSets[k][i] = texCoordSets[k][(ir[i]).get(j++)];
+ }
+ }
+
+ // Replace old arrays with new arrays
+ coordinates = newCoords;
+ colors3 = newColors3;
+ colors4 = newColors4;
+ normals = newNormals;
+ texCoordSets = newTexCoordSets;
+ coordinateIndices = coordOnlyIndices;
+ colorIndices = null;
+ normalIndices = null;
+ texCoordIndexSets = new int[texCoordSetCount][];
+
+ coordOnly = true;
+ } else if (coordOnly) {
+ // Need to change from useCoordIndexOnly format to normal
+ // indexed format. Should make a more efficient implementation
+ // later.
+
+ int n = coordinateIndices.length;
+ if ((colors3 != null) || (colors4 != null)) {
+ colorIndices = new int[n];
+ for (int i = 0 ; i < n ; i++) colorIndices[i] = coordinateIndices[i];
+ }
+ if (normals != null) {
+ normalIndices = new int[n];
+ for (int i = 0 ; i < n ; i++) normalIndices[i] = coordinateIndices[i];
+ }
+ texCoordIndexSets = new int[texCoordSetCount][];
+ for (int i = 0 ; i < texCoordSetCount ; i++) {
+ texCoordIndexSets[i] = new int[n];
+ for (int j = 0 ; j < n ; j++) {
+ texCoordIndexSets[i][j] = coordinateIndices[j];
+ }
+ }
+ coordOnly = false;
+ } else {
+
+ // No need to indexify if already indexed
+ if (coordinateIndices != null) return;
+
+ coordinateIndices = getListIndices(coordinates);
+
+ if (colors3 != null) colorIndices = getListIndices(colors3);
+ else if (colors4 != null) colorIndices = getListIndices(colors4);
+
+ if (normals != null) normalIndices = getListIndices(normals);
+
+ texCoordIndexSets = new int[texCoordSetCount][];
+ for(int i = 0 ; i < texCoordSetCount ; i++) {
+ texCoordIndexSets[i] = getListIndices(texCoordSets[i]);
+ }
+
+ coordOnly = false;
+ }
+
+ if ((DEBUG & 1) == 1) {
+ System.out.println("Coordinate Array:");
+ for (int i = 0 ; i < coordinates.length ; i++) {
+ System.out.println(" " + i + " " + coordinates[i] +
+ " " + coordinates[i].hashCode());
+ }
+ System.out.println("Index array:");
+ for (int i = 0 ; i < coordinateIndices.length ; i++) {
+ System.out.println(" " + i + " " + coordinateIndices[i]);
+ }
+ }
+
+ } // End of indexify
+
+
+
+ public void indexify()
+ {
+ indexify(false);
+ } // End of indexify()
+
+
+
+ /**
+ * Allocates an array of the same type as the input type. This allows us to
+ * use a generic compactData method.
+ *
+ * @param data Array of coordinate, color, normal or texture coordinate data
+ * The data can be in one of the following formats - Point3f, Color3f,
+ * Color4f, TexCoord2f, TexCoord3f, TexCoord4f.
+ *
+ * @param num The size of the array to be allocated
+ *
+ * @return An array of size num of the same type as the input type
+ *
+ * @exception IllegalArgumentException if the input array is not one of the
+ * types listed above.
+ */
+ Object[] allocateArray(Object data[], int num) {
+ Object newData[] = null;
+ if (data instanceof javax.vecmath.Point3f[]) {
+ newData = new Point3f[num];
+ } else if (data instanceof javax.vecmath.Vector3f[]) {
+ newData = new Vector3f[num];
+ } else if (data instanceof javax.vecmath.Color3f[]) {
+ newData = new Color3f[num];
+ } else if (data instanceof javax.vecmath.Color4f[]) {
+ newData = new Color4f[num];
+ } else if (data instanceof javax.vecmath.TexCoord2f[]) {
+ newData = new TexCoord2f[num];
+ } else if (data instanceof javax.vecmath.TexCoord3f[]) {
+ newData = new TexCoord3f[num];
+ } else if (data instanceof javax.vecmath.TexCoord4f[]) {
+ newData = new TexCoord4f[num];
+ } else if (data instanceof IndexRow[]) {
+ // Hack so we can use compactData for coordIndexOnly
+ newData = new IndexRow[num];
+ } else throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo9"));
+ return newData;
+ } // End of allocateArray
+
+
+
+ /**
+ * Generic method that compacts (ie removes unreferenced/duplicate data)
+ * any type of indexed data.
+ * Used to compact coordinate, color, normal and texture coordinate data.
+ * @param indices Array of indices
+ * @param data Array of coordinate, color, normal or texture coordinate data
+ * The data can be in one of the following formats - Point3f, Color3f,
+ * Color4f, TexCoord2f, TexCoord3f, TexCoord4f.
+ * @param newInd The new array of indexes after the data has been compacted.
+ * This must be allocated by the calling method. On return, this array will
+ * contain the new index data. The size of this array must be equal to
+ * indices.length
+ * @return Array of the data with unreferenced and duplicate entries removed.
+ * The return type will be the same as the type that was passed in data.
+ */
+ // TODO: Remove duplicate entries in data lists.
+ private Object[] compactData(int indices[], Object data[], int newInd[]) {
+ Object newData[] = null;
+ /*
+ * This is a three step process.
+ * First, find out how many unique indexes are used. This
+ * will be the size of the new data array.
+ */
+ int numUnique = 0;
+ int translationTable[] = new int[data.length];
+ for (int i = 0 ; i < indices.length ; i++) {
+ if (translationTable[indices[i]] == 0) {
+
+ numUnique++;
+ translationTable[indices[i]] = 1;
+ }
+ }
+ /*
+ * Second, build the new data list. Remember the new indexes so
+ * we can use the table to translate the old indexes to the new
+ */
+ newData = allocateArray(data, numUnique);
+ int newIdx = 0;
+ for (int i = 0 ; i < translationTable.length ; i++) {
+ if (translationTable[i] != 0) {
+ newData[newIdx] = data[i];
+ translationTable[i] = newIdx++;
+ }
+ }
+ /*
+ * Third, make the new index list
+ */
+ for (int i = 0 ; i < indices.length ; i++) {
+ newInd[i] = translationTable[indices[i]];
+ }
+ return newData;
+ } // End of compactData
+
+
+
+ /**
+ * Remove unused data from an indexed dataset.
+ * Indexed data may contain data entries that are never referenced by
+ * the dataset. This routine will remove those entries where
+ * appropriate and renumber the indices to match the new values.
+ * @throws IllegalArgumentException if coordinate data is missing,
+ * if the index lists aren't all the
+ * same length, if an index list is set and the corresponding data
+ * list isn't set, if a data list is set and the corresponding
+ * index list is unset (unless all index lists are unset or in
+ * USE_COORD_INDEX_ONLY format),
+ * if StripCounts or ContourCounts is inconsistent with the current
+ * primitive, if the sum of the contourCounts array doesn't equal
+ * the length of the StripCounts array, or if the number of vertices
+ * isn't a multiple of three (for triangles) or four (for quads).
+ */
+ public void compact()
+ {
+ checkForBadData();
+
+ // Only usable on indexed data
+ if (coordinateIndices == null) return;
+
+ // USE_COORD_INDEX_ONLY never has unused data
+ if (coordOnly) return;
+
+ int newInd[] = new int[coordinateIndices.length];
+ coordinates =
+ (Point3f[])compactData(coordinateIndices, coordinates, newInd);
+ coordinateIndices = newInd;
+
+ if (colorIndices != null) {
+ newInd = new int[colorIndices.length];
+ if (colors3 != null)
+ colors3 = (Color3f[])compactData(colorIndices, colors3, newInd);
+ else if (colors4 != null)
+ colors4 = (Color4f[])compactData(colorIndices, colors4, newInd);
+ colorIndices = newInd;
+ }
+
+ if (normalIndices != null) {
+ newInd = new int[normalIndices.length];
+ normals = (Vector3f[])compactData(normalIndices, normals, newInd);
+ normalIndices = newInd;
+ }
+
+ for (int i = 0 ; i < texCoordSetCount ; i++) {
+ newInd = new int[texCoordIndexSets[i].length];
+ texCoordSets[i] = compactData(texCoordIndexSets[i],
+ texCoordSets[i], newInd);
+ texCoordIndexSets[i] = newInd;
+ }
+ } // End of compact
+
+
+
+ /**
+ * Check the data to make sure everything's consistent.
+ */
+ private void checkForBadData() {
+ boolean badData = false;
+
+ //
+ // Coordinates are required
+ //
+ if (coordinates == null) {
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo3"));
+ }
+
+ //
+ // Check for indices with no data
+ //
+ if ((colors3 == null) && (colors4 == null) && (colorIndices != null))
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo4"));
+ if ((normals == null) && (normalIndices != null))
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo11"));
+
+ //
+ // Make sure all TextureCoordinate data is set (indices or not)
+ //
+ for (int i = 0 ; i < texCoordSetCount ; i++) {
+ if (texCoordSets[i] == null)
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo10"));
+ }
+
+ //
+ // Check for Missing Index lists
+ //
+ boolean texInds = false; // Indicates whether we have texcoord indices
+ if (texCoordIndexSets != null) {
+ for (int i = 0 ; i < texCoordSetCount ; i++) {
+ if (texCoordIndexSets[i] != null) texInds = true;
+ }
+ }
+ if ((coordinateIndices != null) ||
+ (colorIndices != null) ||
+ (normalIndices != null) ||
+ texInds) {
+ // At least one index list is present, so they all must be
+ // present (unless coordOnly)
+ if (coordinateIndices == null) badData = true;
+ else if (coordOnly) {
+ if ((colorIndices != null) ||
+ (normalIndices != null) ||
+ (texInds == true)) {
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo20"));
+ }
+ } else if (((colors3 != null) || (colors4 != null)) &&
+ (colorIndices == null)) badData = true;
+ else if ((normals != null) && (normalIndices == null)) badData = true;
+ else if ((texCoordSetCount > 0) && !texInds) badData = true;
+ if (badData) throw new
+ IllegalArgumentException(J3dUtilsI18N.getString("GeometryInfo19"));
+ }
+
+ //
+ // Make sure index lists are all the same length
+ //
+ if ((coordinateIndices != null) && (!coordOnly)) {
+ if (((colors3 != null) || (colors4 != null)) &&
+ (colorIndices.length != coordinateIndices.length))
+ badData = true;
+ else if ((normals != null) &&
+ (normalIndices.length != coordinateIndices.length))
+ badData = true;
+ else {
+ //Check all texCoord indices have the same length
+ for (int i = 0 ; i < texCoordSetCount ; i++) {
+ if (texCoordIndexSets[i].length != coordinateIndices.length) {
+ badData = true;
+ break;
+ }
+ }
+ }
+ if (badData) {
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo5"));
+ }
+ }
+
+ //
+ // For stripped primitives, make sure we have strip counts
+ //
+ if ((prim == TRIANGLE_STRIP_ARRAY) ||
+ (prim == TRIANGLE_FAN_ARRAY) ||
+ (prim == POLYGON_ARRAY)) {
+ if (stripCounts == null) badData = true;
+ } else if (stripCounts != null) badData = true;
+ if (badData) {
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo6"));
+ }
+
+ // Find out how much data we have
+ int count;
+ if (coordinateIndices == null) count = coordinates.length;
+ else count = coordinateIndices.length;
+
+ //
+ // Make sure sum of strip counts equals indexCount (or vertexCount)
+ // and check to make sure triangles and quads have the right number
+ // of vertices
+ //
+ if ((prim == TRIANGLE_STRIP_ARRAY) ||
+ (prim == TRIANGLE_FAN_ARRAY) ||
+ (prim == POLYGON_ARRAY)) {
+ int sum = 0;
+ for (int i = 0 ; i < stripCounts.length ; i++) {
+ sum += stripCounts[i];
+ }
+ if (sum != count) {
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo7"));
+ }
+ } else if (prim == TRIANGLE_ARRAY) {
+ if (count % 3 != 0) {
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo12"));
+ }
+ } else if (prim == QUAD_ARRAY) {
+ if (count % 4 != 0) {
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo13"));
+ }
+ }
+
+ //
+ // For polygons, make sure the contours add up.
+ //
+ if (prim == POLYGON_ARRAY) {
+ if (contourCounts != null) {
+ int c = 0;
+ for (int i = 0 ; i < contourCounts.length ; i++)
+ c += contourCounts[i];
+ if (c != stripCounts.length) {
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo8"));
+ }
+ }
+ } else {
+ if (contourCounts != null) {
+ throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfo14"));
+ }
+ }
+ } // End of checkForBadData
+
+
+
+ /**
+ * Get rid of index lists by reorganizing data into an un-indexed
+ * format. Does nothing if no index lists are set.
+ * @throws IllegalArgumentException if coordinate data is missing,
+ * if the index lists aren't all the
+ * same length, if an index list is set and the corresponding data
+ * list isn't set, if a data list is set and the corresponding
+ * index list is unset (unless all index lists are unset or in
+ * USE_COORD_INDEX_ONLY format),
+ * if StripCounts or ContourCounts is inconsistent with the current
+ * primitive, if the sum of the contourCounts array doesn't equal
+ * the length of the StripCounts array, or if the number of vertices
+ * isn't a multiple of three (for triangles) or four (for quads).
+ */
+ public void unindexify() {
+ checkForBadData();
+ if (coordinateIndices != null) {
+ // Switch from USE_COORD_INDEX_ONLY format
+ if (coordOnly) indexify(false);
+
+ coordinates =
+ (Point3f[])unindexifyData(coordinates, coordinateIndices);
+ coordinateIndices = null;
+
+ if (colors3 != null) {
+ colors3 = (Color3f[])unindexifyData(colors3, colorIndices);
+ } else if (colors4 != null) {
+ colors4 = (Color4f[])unindexifyData(colors4, colorIndices);
+ }
+ colorIndices = null;
+
+ if (normals != null) {
+ normals = (Vector3f[])unindexifyData(normals, normalIndices);
+ normalIndices = null;
+ }
+
+ for (int i = 0 ; i < texCoordSetCount ; i++)
+ texCoordSets[i] = unindexifyData(texCoordSets[i],
+ texCoordIndexSets[i]);
+ texCoordIndexSets = new int[texCoordSetCount][];
+ }
+ } // End of unindexify
+
+
+
+ /**
+ * Generic unindexify method. Can unindex data in any of the following
+ * formats Point3f, Color3f, Color4f, Vector3f, TexCoord2f, TexCoord3f,
+ * TexCoord4f.
+ */
+ private Object[] unindexifyData(Object data[], int index[])
+ {
+ Object newData[] = allocateArray(data, index.length);
+ for (int i = 0 ; i < index.length ; i++) {
+ newData[i] = data[index[i]];
+ }
+ return newData;
+ } // End of unindexifyData
+
+
+
+ /**
+ * Calculate vertexFormat based on data.
+ */
+ private int getVertexFormat()
+ {
+ int vertexFormat = GeometryArray.COORDINATES;
+
+ if (colors3 != null) vertexFormat |= GeometryArray.COLOR_3;
+ else if (colors4 != null) vertexFormat |= GeometryArray.COLOR_4;
+
+ if (normals != null) vertexFormat |= GeometryArray.NORMALS;
+
+ if (texCoordDim == 2)
+ vertexFormat |= GeometryArray.TEXTURE_COORDINATE_2;
+ else if (texCoordDim == 3)
+ vertexFormat |= GeometryArray.TEXTURE_COORDINATE_3;
+ else if (texCoordDim == 4)
+ vertexFormat |= GeometryArray.TEXTURE_COORDINATE_4;
+
+ return vertexFormat;
+ } // End of getVertexFormat
+
+
+
+ /**
+ * Calculate vertexCount based on data
+ */
+ private int getVertexCount()
+ {
+ int vertexCount = coordinates.length;
+
+ if (colors3 != null) {
+ if (colors3.length > vertexCount) vertexCount = colors3.length;
+ } else if (colors4 != null) {
+ if (colors4.length > vertexCount) vertexCount = colors4.length;
+ }
+
+ if (normals != null) {
+ if (normals.length > vertexCount) vertexCount = normals.length;
+ }
+
+ // Find max length tex coord set
+ for (int i = 0 ; i < texCoordSetCount ; i++) {
+ if (texCoordSets[i].length > vertexCount)
+ vertexCount = texCoordSets[i].length;
+ }
+
+ return vertexCount;
+ } // End of getVertexCount
+
+
+
+ /**
+ * Converts an array of Tuple2f, Tuple3f, or Tuple4f values into
+ * an array of floats. Assumes array is not null. Returns null
+ * if array is not Tuple2f, Tuple3f, or Tuple4f. Used by fillIn()
+ * for BY_REFERENCE not INTERLEAVED geometry.
+ */
+ private float[] vecmathToFloat(Object[] ar)
+ {
+ if (ar[0] instanceof Tuple2f) {
+ float[] p = new float[ar.length * 2];
+ Tuple2f[] a = (Tuple2f[])ar;
+ for (int i = 0 ; i < ar.length ; i++) {
+ p[i * 2] = a[i].x;
+ p[i * 2 + 1] = a[i].y;
+ }
+ return p;
+ } else if (ar[0] instanceof Tuple3f) {
+ float[] p = new float[ar.length * 3];
+ Tuple3f[] a = (Tuple3f[])ar;
+ for (int i = 0 ; i < ar.length ; i++) {
+ p[i * 3] = a[i].x;
+ p[i * 3 + 1] = a[i].y;
+ p[i * 3 + 2] = a[i].z;
+ }
+ return p;
+ } else if (ar[0] instanceof Tuple4f) {
+ float[] p = new float[ar.length * 4];
+ Tuple4f[] a = (Tuple4f[])ar;
+ for (int i = 0 ; i < ar.length ; i++) {
+ p[i * 4] = a[i].x;
+ p[i * 4 + 1] = a[i].y;
+ p[i * 4 + 2] = a[i].z;
+ p[i * 4 + 3] = a[i].w;
+ }
+ return p;
+ }
+ return null;
+ } // End of vecmathToFloat
+
+
+
+ /**
+ * Fill in the GeometryArray object. Used by getGeometryArray and
+ * getIndexedGeometryArray. checkForBadData has already been called.
+ */
+ private void fillIn(GeometryArray ga, boolean byRef, boolean interleaved,
+ boolean nio)
+ {
+ if (interleaved) {
+ // Calculate number of words per vertex
+ int wpv = 3; // Always have coordinate data
+ if (normals != null) wpv += 3;
+ if (colors3 != null) wpv += 3;
+ else if (colors4 != null) wpv += 4;
+ wpv += (texCoordSetCount * texCoordDim);
+
+ // Build array of interleaved data
+ float[] d = new float[wpv * coordinates.length];
+
+ // Fill in the array
+ int offset = 0;
+ for (int i = 0 ; i < coordinates.length ; i++) {
+ if (texCoordDim == 2) {
+ for (int j = 0 ; j < texCoordSetCount ; j++) {
+ d[offset++] = ((TexCoord2f)texCoordSets[j][i]).x;
+ d[offset++] = ((TexCoord2f)texCoordSets[j][i]).y;
+ }
+ } else if (texCoordDim == 3) {
+ for (int j = 0 ; j < texCoordSetCount ; j++) {
+ d[offset++] = ((TexCoord3f)texCoordSets[j][i]).x;
+ d[offset++] = ((TexCoord3f)texCoordSets[j][i]).y;
+ d[offset++] = ((TexCoord3f)texCoordSets[j][i]).z;
+ }
+ } else if (texCoordDim == 4) {
+ for (int j = 0 ; j < texCoordSetCount ; j++) {
+ d[offset++] = ((TexCoord4f)texCoordSets[j][i]).x;
+ d[offset++] = ((TexCoord4f)texCoordSets[j][i]).y;
+ d[offset++] = ((TexCoord4f)texCoordSets[j][i]).z;
+ d[offset++] = ((TexCoord4f)texCoordSets[j][i]).w;
+ }
+ }
+
+ if (colors3 != null) {
+ d[offset++] = colors3[i].x;
+ d[offset++] = colors3[i].y;
+ d[offset++] = colors3[i].z;
+ } else if (colors4 != null) {
+ d[offset++] = colors4[i].x;
+ d[offset++] = colors4[i].y;
+ d[offset++] = colors4[i].z;
+ d[offset++] = colors4[i].w;
+ }
+
+ if (normals != null) {
+ d[offset++] = normals[i].x;
+ d[offset++] = normals[i].y;
+ d[offset++] = normals[i].z;
+ }
+
+ d[offset++] = coordinates[i].x;
+ d[offset++] = coordinates[i].y;
+ d[offset++] = coordinates[i].z;
+ }
+ // Register reference to array of interleaved data
+ if (nio) {
+ ByteBufferWrapper b = ByteBufferWrapper.allocateDirect(d.length * 4);
+ FloatBufferWrapper f =
+ b.order( ByteOrderWrapper.nativeOrder() ).asFloatBuffer();
+ f.put(d);
+ ga.setInterleavedVertexBuffer(f.getJ3DBuffer());
+ } else ga.setInterleavedVertices(d);
+ } else if (nio) {
+
+ ByteBufferWrapper b =
+ ByteBufferWrapper.allocateDirect(coordinates.length * 4 * 3);
+ FloatBufferWrapper f =
+ b.order( ByteOrderWrapper.nativeOrder() ).asFloatBuffer();
+ f.put(vecmathToFloat(coordinates));
+ ga.setCoordRefBuffer(f.getJ3DBuffer());
+
+ if (colors3 != null) {
+ b = ByteBufferWrapper.allocateDirect(colors3.length * 4 * 3);
+ f = b.order( ByteOrderWrapper.nativeOrder() ).asFloatBuffer();
+ f.put(vecmathToFloat(colors3));
+ ga.setColorRefBuffer(f.getJ3DBuffer());
+ } else if (colors4 != null) {
+ b = ByteBufferWrapper.allocateDirect(colors4.length * 4 * 4);
+ f = b.order( ByteOrderWrapper.nativeOrder() ).asFloatBuffer();
+ f.put(vecmathToFloat(colors4));
+ ga.setColorRefBuffer(f.getJ3DBuffer());
+ }
+
+ if (normals != null) {
+ b = ByteBufferWrapper.allocateDirect(normals.length * 4 * 3);
+ f = b.order( ByteOrderWrapper.nativeOrder() ).asFloatBuffer();
+ f.put(vecmathToFloat(normals));
+ ga.setNormalRefBuffer(f.getJ3DBuffer());
+ }
+
+ for (int i = 0 ; i < texCoordSetCount ; i++) {
+ b = ByteBufferWrapper.allocateDirect(
+ texCoordSets[i].length * 4 * texCoordDim);
+ f = b.order( ByteOrderWrapper.nativeOrder() ).asFloatBuffer();
+ f.put(vecmathToFloat(texCoordSets[i]));
+ ga.setTexCoordRefBuffer(i, f.getJ3DBuffer());
+ }
+ } else if (byRef) {
+ // Need to copy the data into float arrays - GeometryArray
+ // prefers them over the vecmath types
+ ga.setCoordRefFloat(vecmathToFloat(coordinates));
+ if (colors3 != null) ga.setColorRefFloat(vecmathToFloat(colors3));
+ else if (colors4 != null) ga.setColorRefFloat(vecmathToFloat(colors4));
+ if (normals != null) ga.setNormalRefFloat(vecmathToFloat(normals));
+ for (int i = 0 ; i < texCoordSetCount ; i++) {
+ ga.setTexCoordRefFloat(i, vecmathToFloat(texCoordSets[i]));
+ }
+ } else {
+ ga.setCoordinates(0, coordinates);
+ if (colors3 != null) ga.setColors(0, colors3);
+ else if (colors4 != null) ga.setColors(0, colors4);
+ if (normals != null) ga.setNormals(0, normals);
+ for (int i = 0 ; i < texCoordSetCount ; i++) {
+ if (texCoordDim == 2) {
+ ga.setTextureCoordinates(i, 0, (TexCoord2f[])texCoordSets[i]);
+ } else if (texCoordDim == 3) {
+ ga.setTextureCoordinates(i, 0, (TexCoord3f[])texCoordSets[i]);
+ } else if (texCoordDim == 4) {
+ ga.setTextureCoordinates(i, 0, (TexCoord4f[])texCoordSets[i]);
+ }
+ }
+ }
+
+ if (coordinateIndices != null) {
+ IndexedGeometryArray iga = null;
+ iga = (IndexedGeometryArray)ga;
+ iga.setCoordinateIndices(0, coordinateIndices);
+ if (!coordOnly) {
+ if (colorIndices != null) iga.setColorIndices(0, colorIndices);
+ if (normalIndices != null) iga.setNormalIndices(0, normalIndices);
+ for (int i = 0 ; i < texCoordSetCount ; i++)
+ iga.setTextureCoordinateIndices(i, 0, texCoordIndexSets[i]);
+ }
+ }
+ } // End of fillIn
+
+
+
+ /**
+ * Redo indexes to guarantee connection information.
+ * Use this routine if your original data is in indexed format, but
+ * you don't trust that the indexing is correct. After this
+ * routine it is guaranteed that two points with the same
+ * position will have the same coordinate index (for example).
+ * Try this if you see
+ * glitches in your normals or stripification, to rule out
+ * bad indexing as the source of the problem. Works with normal
+ * indexed format or USE_COORD_INDEX_ONLY format.
+ * @throws IllegalArgumentException if coordinate data is missing,
+ * if the index lists aren't all the
+ * same length, if an index list is set and the corresponding data
+ * list isn't set, if a data list is set and the corresponding
+ * index list is unset (unless all index lists are unset or in
+ * USE_COORD_INDEX_ONLY format),
+ * if StripCounts or ContourCounts is inconsistent with the current
+ * primitive, if the sum of the contourCounts array doesn't equal
+ * the length of the StripCounts array, or if the number of vertices
+ * isn't a multiple of three (for triangles) or four (for quads).
+ */
+ public void recomputeIndices()
+ {
+ boolean remember = coordOnly;
+
+ // Can make more efficient implementation later
+ unindexify();
+ indexify(remember);
+ } // End of recomputeIndices
+
+
+
+ /**
+ * Reverse the order of an array of ints (computer class homework
+ * problem).
+ */
+ private void reverseList(int list[])
+ {
+ int t;
+
+ if (list == null) return;
+
+ for (int i = 0 ; i < list.length / 2 ; i++) {
+ t = list[i];
+ list[i] = list[list.length - i - 1];
+ list[list.length - i - 1] = t;
+ }
+ } // End of reverseList
+
+
+
+ /**
+ * Reverse the order of all lists. If your polygons are formatted with
+ * clockwise winding, you will always see the back and never the front.
+ * (Java 3D always wants vertices specified with a counter-clockwise
+ * winding.)
+ * This method will (in effect) reverse the winding of your data by
+ * inverting all of the index lists and the stripCounts
+ * and contourCounts lists.
+ * @throws IllegalArgumentException if coordinate data is missing,
+ * if the index lists aren't all the
+ * same length, if an index list is set and the corresponding data
+ * list isn't set, if a data list is set and the corresponding
+ * index list is unset (unless all index lists are unset or in
+ * USE_COORD_INDEX_ONLY format),
+ * if StripCounts or ContourCounts is inconsistent with the current
+ * primitive, if the sum of the contourCounts array doesn't equal
+ * the length of the StripCounts array, or if the number of vertices
+ * isn't a multiple of three (for triangles) or four (for quads).
+ */
+ public void reverse()
+ {
+ indexify();
+ reverseList(stripCounts);
+ reverseList(oldStripCounts);
+ reverseList(contourCounts);
+ reverseList(coordinateIndices);
+ reverseList(colorIndices);
+ reverseList(normalIndices);
+ for (int i = 0 ; i < texCoordSetCount ; i++)
+ reverseList(texCoordIndexSets[i]);
+ } // End of reverse
+
+
+
+ /**
+ * Returns true if the data in this GeometryInfo is currently
+ * formatted in the USE_COORD_INDEX_ONLY format where a single
+ * index list is used to index into all data lists.
+ * @see GeometryInfo#indexify(boolean)
+ * @see GeometryInfo#getIndexedGeometryArray(boolean, boolean, boolean,
+ * boolean, boolean)
+ */
+ public boolean getUseCoordIndexOnly()
+ {
+ return coordOnly;
+ } // End of getUseCoordIndexOnly
+
+
+
+ /**
+ * Tells the GeometryInfo that its data is formatted in the
+ * USE_COORD_INDEX_ONLY format with a single index list
+ * (the coordinate index list) that indexes into all data
+ * lists (coordinates, normals, colors, and texture
+ * coordinates). NOTE: this will not convert the data
+ * for you. This method is for when you are sending in
+ * data useng the setCoordinates, setNormals, setColors,
+ * and/or setTextureCoordinates methods, and you are only
+ * setting one index using setCoordinateIndices(). If
+ * you want GeometryInfo to convert your data to the
+ * USE_COORD_INDEX_ONLY format, use indexify(true) or
+ * getIndexedGeometryArray with the useCoordIndexOnly
+ * parameter set to true.
+ * @see GeometryInfo#indexify(boolean)
+ * @see GeometryInfo#getIndexedGeometryArray(boolean, boolean, boolean,
+ * boolean, boolean)
+ */
+ public void setUseCoordIndexOnly(boolean useCoordIndexOnly)
+ {
+ coordOnly = useCoordIndexOnly;
+ } // End of setUseCoordIndexOnly
+
+
+
+ /**
+ * Creates and returns a non-indexed Java 3D GeometryArray object
+ * based on the data in the GeometryInfo object. This object is
+ * suitable to be attached to a Shape3D node for rendering.
+ * @param byRef Use geometry BY_REFERENCE
+ * @param interleaved Use INTERLEAVED geometry. Implies byRef is
+ * true as well.
+ * @param nio Create GeometryArray using java.nio.Buffer for
+ * geometry arrays. Only usable on JDK 1.4 or higher. Implies
+ * byRef is true as well.
+ * @throws IllegalArgumentException if coordinate data is missing,
+ * if the index lists aren't all the
+ * same length, if an index list is set and the corresponding data
+ * list isn't set, if a data list is set and the corresponding
+ * index list is unset (unless all index lists are unset or in
+ * USE_COORD_INDEX_ONLY format),
+ * if StripCounts or ContourCounts is inconsistent with the current
+ * primitive, if the sum of the contourCounts array doesn't equal
+ * the length of the StripCounts array, or if the number of vertices
+ * isn't a multiple of three (for triangles) or four (for quads).
+ */
+ public GeometryArray getGeometryArray(boolean byRef, boolean interleaved,
+ boolean nio)
+ {
+ checkForBadData();
+
+ if (prim == POLYGON_ARRAY) {
+ if (tr == null) tr = new Triangulator();
+ tr.triangulate(this);
+ } else changeBackToOldPrim();
+
+ unindexify();
+
+ int vertexFormat = getVertexFormat();
+ if (nio) vertexFormat |= (GeometryArray.BY_REFERENCE |
+ GeometryArray.USE_NIO_BUFFER);
+ if (interleaved) vertexFormat |= (GeometryArray.BY_REFERENCE |
+ GeometryArray.INTERLEAVED);
+ if (byRef) vertexFormat |= GeometryArray.BY_REFERENCE;
+
+ int vertexCount = coordinates.length;
+
+ // If the texCoordSetMap hasn't been set, assume one set of
+ // texture coordinates only and one texture state unit
+ if ((texCoordSetCount > 0) && (texCoordSetMap == null)) {
+ texCoordSetCount = 1;
+ texCoordSetMap = new int[1];
+ texCoordSetMap[0] = 0;
+ }
+
+ // Create the GeometryArray object
+ GeometryArray ga = null;
+ switch (prim) {
+ case TRIANGLE_ARRAY:
+ TriangleArray ta = new TriangleArray(vertexCount, vertexFormat,
+ texCoordSetCount, texCoordSetMap);
+ ga = (GeometryArray)ta;
+ break;
+
+ case QUAD_ARRAY:
+ QuadArray qa = new QuadArray(vertexCount, vertexFormat,
+ texCoordSetCount, texCoordSetMap);
+ ga = (GeometryArray)qa;
+ break;
+
+ case TRIANGLE_STRIP_ARRAY:
+ TriangleStripArray tsa = new TriangleStripArray(vertexCount,
+ vertexFormat, texCoordSetCount, texCoordSetMap,
+ stripCounts);
+ ga = (GeometryArray)tsa;
+ break;
+
+ case TRIANGLE_FAN_ARRAY:
+ TriangleFanArray tfa = new TriangleFanArray(vertexCount,
+ vertexFormat, texCoordSetCount, texCoordSetMap,
+ stripCounts);
+ ga = (GeometryArray)tfa;
+ break;
+ }
+
+ fillIn(ga, byRef, interleaved, nio);
+
+ return ga;
+ } // End of getGeometryArray(int, int)
+
+
+
+ /**
+ * Creates and returns a non-indexed Java 3D GeometryArray object
+ * based on the data in the GeometryInfo object. This object is
+ * suitable to be attached to a Shape3D node for rendering.
+ * The geometry is <b>not</b> created using data BY_REFERENCE,
+ * INTERLEAVED, or USE_NIO_BUFFER.
+ * @throws IllegalArgumentException if coordinate data is missing,
+ * if the index lists aren't all the
+ * same length, if an index list is set and the corresponding data
+ * list isn't set, if a data list is set and the corresponding
+ * index list is unset (unless all index lists are unset or in
+ * USE_COORD_INDEX_ONLY format),
+ * if StripCounts or ContourCounts is inconsistent with the current
+ * primitive, if the sum of the contourCounts array doesn't equal
+ * the length of the StripCounts array, or if the number of vertices
+ * isn't a multiple of three (for triangles) or four (for quads).
+ */
+ public GeometryArray getGeometryArray()
+ {
+ return getGeometryArray(false, false, false);
+ } // End of getGeometryArray()
+
+
+
+ /**
+ * Creates and returns a IndexedGeometryArray
+ * based on the data in the GeometryInfo object. This object is
+ * suitable to be attached to a Shape3D node for rendering.
+ * @param compact Remove Coordinates, Colors, Normals, and
+ * TextureCoordinates that aren't referenced by any indices.
+ * @param byRef Create the IndexedGeometryArray using geometry
+ * BY_REFERENCE.
+ * @param interleaved Use INTERLEAVED geometry. Implies byRef is
+ * true as well.
+ * @param nio Create GeometryArray using java.nio.Buffer for
+ * geometry arrays. Only usable on JDK 1.4 or higher. Implies
+ * byRef is true as well.
+ * @param useCoordIndexOnly Create the IndexedGeometryArray using
+ * USE_COORD_INDEX_ONLY. Values from the coordinate index array
+ * are used as a single set of indices into all vertex
+ * component arrays (coord, color, normal, and texCoord).
+ * @throws IllegalArgumentException if coordinate data is missing,
+ * if the index lists aren't all the
+ * same length, if an index list is set and the corresponding data
+ * list isn't set, if a data list is set and the corresponding
+ * index list is unset (unless all index lists are unset or in
+ * USE_COORD_INDEX_ONLY format),
+ * if StripCounts or ContourCounts is inconsistent with the current
+ * primitive, if the sum of the contourCounts array doesn't equal
+ * the length of the StripCounts array, or if the number of vertices
+ * isn't a multiple of three (for triangles) or four (for quads).
+ */
+ public IndexedGeometryArray getIndexedGeometryArray(boolean compact,
+ boolean byRef,
+ boolean interleaved,
+ boolean useCoordIndexOnly,
+ boolean nio)
+ {
+ indexify(useCoordIndexOnly);
+
+ if (compact) compact();
+
+ if (prim == POLYGON_ARRAY) {
+ if (tr == null) tr = new Triangulator();
+ tr.triangulate(this);
+ } else changeBackToOldPrim();
+
+ if (useCoordIndexOnly && coordOnly == false) {
+ // Check to see if we can optimize for USE_COORD_INDEX_ONLY
+ int i, j;
+ boolean canUseCoordIndexOnly = true;
+
+ if (coordinateIndices != null) {
+ // See if all the array lengths are the same
+ if (colorIndices != null &&
+ colorIndices.length != coordinateIndices.length) {
+ canUseCoordIndexOnly = false;
+ }
+ if (normalIndices != null &&
+ normalIndices.length != coordinateIndices.length) {
+ canUseCoordIndexOnly = false;
+ }
+ for (i = 0 ; i < texCoordSetCount ; i++) {
+ if (texCoordIndexSets[i] != null &&
+ texCoordIndexSets[i].length != coordinateIndices.length) {
+ canUseCoordIndexOnly = false;
+ break;
+ }
+ }
+ if (canUseCoordIndexOnly &&
+ ((colorIndices != null) ||
+ (normalIndices != null) ||
+ (texCoordSetCount > 0))) {
+ // All array lengths are the same. Check their contents
+
+ for (i=0; i<coordinateIndices.length; i++) {
+ int indexValue = coordinateIndices[i];
+
+
+ if (colorIndices != null &&
+ colorIndices[i] != indexValue) {
+ canUseCoordIndexOnly = false;
+ break;
+ }
+ if (normalIndices != null &&
+ normalIndices[i] != indexValue) {
+ canUseCoordIndexOnly = false;
+ break;
+ }
+ for (j=0; j<texCoordSetCount; j++) {
+ if (texCoordIndexSets[j] != null &&
+ texCoordIndexSets[j][i] != indexValue) {
+ canUseCoordIndexOnly = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ coordOnly = canUseCoordIndexOnly;
+ }
+
+ int vertexFormat = getVertexFormat();
+ if (nio) vertexFormat |= (GeometryArray.BY_REFERENCE |
+ GeometryArray.USE_NIO_BUFFER);
+ if (interleaved) vertexFormat |= (GeometryArray.BY_REFERENCE |
+ GeometryArray.INTERLEAVED);
+ if (byRef) vertexFormat |= GeometryArray.BY_REFERENCE;
+ if (coordOnly) vertexFormat |= GeometryArray.USE_COORD_INDEX_ONLY;
+
+ int vertexCount = getVertexCount();
+
+ if ((texCoordSetCount > 0) && (texCoordSetMap == null)) {
+ texCoordSetCount = 1;
+ texCoordSetMap = new int[1];
+ texCoordSetMap[0] = 0;
+ }
+
+ //
+ // Create the IndexedGeometryArray object
+ //
+
+ IndexedGeometryArray ga = null;
+
+ switch (prim) {
+ case TRIANGLE_ARRAY:
+ IndexedTriangleArray ta = new IndexedTriangleArray(vertexCount,
+ vertexFormat, texCoordSetCount, texCoordSetMap,
+ coordinateIndices.length);
+ ga = (IndexedGeometryArray)ta;
+ break;
+
+ case QUAD_ARRAY:
+ IndexedQuadArray qa = new IndexedQuadArray(vertexCount,
+ vertexFormat, texCoordSetCount, texCoordSetMap,
+ coordinateIndices.length);
+ ga = (IndexedGeometryArray)qa;
+ break;
+ case TRIANGLE_STRIP_ARRAY:
+ IndexedTriangleStripArray tsa = new IndexedTriangleStripArray(
+ vertexCount, vertexFormat, texCoordSetCount,
+ texCoordSetMap, coordinateIndices.length, stripCounts);
+ ga = (IndexedGeometryArray)tsa;
+ break;
+
+ case TRIANGLE_FAN_ARRAY:
+ IndexedTriangleFanArray tfa = new IndexedTriangleFanArray(
+ vertexCount, vertexFormat, texCoordSetCount,
+ texCoordSetMap, coordinateIndices.length, stripCounts);
+ ga = (IndexedGeometryArray)tfa;
+ break;
+ }
+
+ // Fill in the GeometryArray object
+ fillIn(ga, byRef, interleaved, nio);
+
+ return ga;
+ } // End of getIndexedGeometryArray(bool, bool, bool, bool, bool)
+
+
+
+ /**
+ * Creates and returns an IndexedGeometryArray
+ * based on the data in the GeometryInfo object. This object is
+ * suitable to be attached to a Shape3D node for rendering.
+ * Equivalent to <code>getIndexedGeometryArray(compact, false,
+ * false, false, false)</code>.
+ * @param compact Remove Coordinates, Colors, Normals, and
+ * TextureCoordinates that aren't referenced by any indices.
+ * @throws IllegalArgumentException if coordinate data is missing,
+ * if the index lists aren't all the
+ * same length, if an index list is set and the corresponding data
+ * list isn't set, if a data list is set and the corresponding
+ * index list is unset (unless all index lists are unset or in
+ * USE_COORD_INDEX_ONLY format),
+ * if StripCounts or ContourCounts is inconsistent with the current
+ * primitive, if the sum of the contourCounts array doesn't equal
+ * the length of the StripCounts array, or if the number of vertices
+ * isn't a multiple of three (for triangles) or four (for quads).
+ */
+ public IndexedGeometryArray getIndexedGeometryArray(boolean compact)
+ {
+ return getIndexedGeometryArray(compact, false, false, false, false);
+ } // End of getIndexedGeometryArray(boolean)
+
+
+
+ /**
+ * Creates and returns an IndexedGeometryArray
+ * based on the data in the GeometryInfo object. This object is
+ * suitable to be attached to a Shape3D node for rendering.
+ * Equivalent to <code>getIndexedGeometryArray(false, false,
+ * false, false, false)</code>.
+ * @throws IllegalArgumentException if coordinate data is missing,
+ * if the index lists aren't all the
+ * same length, if an index list is set and the corresponding data
+ * list isn't set, if a data list is set and the corresponding
+ * index list is unset (unless all index lists are unset or in
+ * USE_COORD_INDEX_ONLY format),
+ * if StripCounts or ContourCounts is inconsistent with the current
+ * primitive, if the sum of the contourCounts array doesn't equal
+ * the length of the StripCounts array, or if the number of vertices
+ * isn't a multiple of three (for triangles) or four (for quads).
+ */
+ public IndexedGeometryArray getIndexedGeometryArray()
+ {
+ return getIndexedGeometryArray(false, false, false, false, false);
+ } // End of getIndexedGeometryArray()
+
+} // End of class GeometryInfo
+
+// End of file GeometryInfo.java
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/GeometryInfoGenerator.java b/src/classes/share/com/sun/j3d/utils/geometry/GeometryInfoGenerator.java
new file mode 100644
index 0000000..dd8b599
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/GeometryInfoGenerator.java
@@ -0,0 +1,857 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry;
+
+import javax.media.j3d.GeometryArray;
+import javax.media.j3d.GeometryStripArray;
+import javax.media.j3d.TriangleFanArray;
+import javax.media.j3d.TriangleStripArray;
+import javax.media.j3d.TriangleArray;
+import javax.media.j3d.QuadArray;
+import javax.media.j3d.IndexedGeometryArray;
+import javax.media.j3d.IndexedGeometryStripArray;
+import javax.media.j3d.IndexedQuadArray;
+import javax.media.j3d.IndexedTriangleArray;
+import javax.media.j3d.IndexedTriangleFanArray;
+import javax.media.j3d.IndexedTriangleStripArray;
+import javax.vecmath.*;
+import com.sun.j3d.utils.geometry.GeometryInfo;
+import com.sun.j3d.internal.J3dUtilsI18N;
+import com.sun.j3d.internal.BufferWrapper;
+import com.sun.j3d.internal.ByteBufferWrapper;
+import com.sun.j3d.internal.FloatBufferWrapper;
+import com.sun.j3d.internal.DoubleBufferWrapper;
+import javax.media.j3d.J3DBuffer;
+
+
+
+/**
+ * Populate a GeometryInfo object from the Geometry provided. Used
+ * by GeometryInfo.
+ */
+class GeometryInfoGenerator extends Object {
+
+ public static void create(GeometryInfo geomInfo, GeometryArray geomArray)
+ {
+ if (geomArray instanceof GeometryStripArray)
+ create(geomInfo, (GeometryStripArray)geomArray);
+ else if (geomArray instanceof TriangleArray) {
+ geomInfo.reset(GeometryInfo.TRIANGLE_ARRAY);
+ processGeometryArray(geomInfo, geomArray);
+ } else if (geomArray instanceof QuadArray) {
+ geomInfo.reset(GeometryInfo.QUAD_ARRAY);
+ processGeometryArray(geomInfo, geomArray);
+ } else if (geomArray instanceof IndexedGeometryArray)
+ create(geomInfo, (IndexedGeometryArray)geomArray);
+ else throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfoGenerator0"));
+ } // End of create(GeometryInfo, GeometryArray)
+
+
+
+ private static void create(GeometryInfo geomInfo,
+ GeometryStripArray geomArray)
+ {
+ if (geomArray instanceof TriangleFanArray) {
+ geomInfo.reset(GeometryInfo.TRIANGLE_FAN_ARRAY);
+ } else if (geomArray instanceof TriangleStripArray) {
+ geomInfo.reset(GeometryInfo.TRIANGLE_STRIP_ARRAY);
+ } else throw new IllegalArgumentException(
+ J3dUtilsI18N.getString("GeometryInfoGenerator0"));
+
+ processGeometryArray(geomInfo, geomArray);
+ processStripArray(geomInfo, geomArray);
+ } // End of create(GeometryInfo, GeometryStripArray)
+
+
+
+ private static void create(GeometryInfo geomInfo,
+ IndexedGeometryArray geomArray)
+ {
+ if (geomArray instanceof IndexedQuadArray) {
+ geomInfo.reset(GeometryInfo.QUAD_ARRAY);
+ } else if (geomArray instanceof IndexedTriangleArray) {
+ geomInfo.reset(GeometryInfo.TRIANGLE_ARRAY);
+ } else if (geomArray instanceof IndexedTriangleFanArray) {
+ geomInfo.reset(GeometryInfo.TRIANGLE_FAN_ARRAY);
+ processIndexStripArray(geomInfo, (IndexedGeometryStripArray)geomArray);
+ } else if (geomArray instanceof IndexedTriangleStripArray) {
+ geomInfo.reset(GeometryInfo.TRIANGLE_STRIP_ARRAY);
+ processIndexStripArray(geomInfo, (IndexedGeometryStripArray)geomArray);
+ }
+
+ processGeometryArray(geomInfo, geomArray);
+ processIndexedArray(geomInfo, geomArray);
+ } // End of create(GeometryInfo, IndexedGeometryArray)
+
+
+
+ private static void processGeometryArray(GeometryInfo geomInfo,
+ GeometryArray geomArray)
+ {
+ int i, j;
+ int vertexFormat = geomArray.getVertexFormat();
+ int texSets = geomArray.getTexCoordSetCount();
+ int valid;
+
+ // Calculate validVertexCount
+ if (geomArray instanceof GeometryStripArray) {
+ // Does not include IndexedGeometryStripArray
+ GeometryStripArray gsa = (GeometryStripArray)geomArray;
+ int[] strips = new int[gsa.getNumStrips()];
+ gsa.getStripVertexCounts(strips);
+ valid = 0;
+ for (i = 0 ; i < strips.length ; i++) {
+ valid += strips[i];
+ }
+ } else if (geomArray instanceof IndexedGeometryArray) {
+ valid = geomArray.getVertexCount();
+ } else valid = geomArray.getValidVertexCount();
+
+ if ((vertexFormat & GeometryArray.INTERLEAVED) != 0) {
+
+ // Calculate words_per_vertex (wpv)
+ int wpv = 3; // Always have coordinate data
+ if ((vertexFormat & GeometryArray.NORMALS) != 0) wpv += 3;
+ if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4)
+ wpv += 4;
+ else if ((vertexFormat & GeometryArray.COLOR_3) != 0) wpv += 3;
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0)
+ wpv += 2 * texSets;
+ else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0)
+ wpv += 3 * texSets;
+ else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0)
+ wpv += 4 * texSets;
+
+ int initial;
+ if (!(geomArray instanceof IndexedGeometryArray)) {
+ initial = geomArray.getInitialVertexIndex();
+ } else initial = 0;
+
+ float[] d;
+ if ((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0) {
+ J3DBuffer b = geomArray.getInterleavedVertexBuffer();
+ FloatBufferWrapper w = new FloatBufferWrapper(b);
+ d = new float[w.limit()];
+ w.position( 0 );
+ w.get(d);
+ } else d = geomArray.getInterleavedVertices();
+
+ int offset = 0;
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) {
+ geomInfo.setTextureCoordinateParams(texSets, 2);
+ int[] map = new int[geomArray.getTexCoordSetMapLength()];
+ geomArray.getTexCoordSetMap(map);
+ geomInfo.setTexCoordSetMap(map);
+ for (i = 0 ; i < texSets ; i++) {
+ TexCoord2f[] tex = new TexCoord2f[valid];
+ for (j = 0 ; j < valid ; j++) {
+ tex[j] = new TexCoord2f(d[wpv * (j + initial) + offset],
+ d[wpv * (j + initial) + offset + 1]);
+ }
+ geomInfo.setTextureCoordinates(i, tex);
+ offset += 2;
+ }
+ } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
+ geomInfo.setTextureCoordinateParams(texSets, 3);
+ int[] map = new int[geomArray.getTexCoordSetMapLength()];
+ geomArray.getTexCoordSetMap(map);
+ geomInfo.setTexCoordSetMap(map);
+ for (i = 0 ; i < texSets ; i++) {
+ TexCoord3f[] tex = new TexCoord3f[valid];
+ for (j = 0 ; j < valid ; j++) {
+ tex[j] = new TexCoord3f(d[wpv * (j + initial) + offset],
+ d[wpv * (j + initial) + offset + 1],
+ d[wpv * (j + initial) + offset + 2]);
+ }
+ geomInfo.setTextureCoordinates(i, tex);
+ offset += 3;
+ }
+ } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
+ geomInfo.setTextureCoordinateParams(texSets, 4);
+ int[] map = new int[geomArray.getTexCoordSetMapLength()];
+ geomArray.getTexCoordSetMap(map);
+ geomInfo.setTexCoordSetMap(map);
+ for (i = 0 ; i < texSets ; i++) {
+ TexCoord4f[] tex = new TexCoord4f[valid];
+ for (j = 0 ; j < valid ; j++) {
+ tex[j] = new TexCoord4f(d[wpv * (j + initial) + offset],
+ d[wpv * (j + initial) + offset + 1],
+ d[wpv * (j + initial) + offset + 2],
+ d[wpv * (j + initial) + offset + 3]);
+ }
+ geomInfo.setTextureCoordinates(i, tex);
+ offset += 4;
+ }
+ }
+
+ if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4) {
+ Color4f[] color = new Color4f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ color[i] = new Color4f(d[wpv * (i + initial) + offset],
+ d[wpv * (i + initial) + offset + 1],
+ d[wpv * (i + initial) + offset + 2],
+ d[wpv * (i + initial) + offset + 3]);
+ }
+ geomInfo.setColors(color);
+ offset += 4;
+ } else if ((vertexFormat & GeometryArray.COLOR_3) != 0) {
+ Color3f[] color = new Color3f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ color[i] = new Color3f(d[wpv * (i + initial) + offset],
+ d[wpv * (i + initial) + offset + 1],
+ d[wpv * (i + initial) + offset + 2]);
+ }
+ geomInfo.setColors(color);
+ offset += 3;
+ }
+
+ if ((vertexFormat & GeometryArray.NORMALS) != 0) {
+ Vector3f[] normals = new Vector3f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ normals[i] = new Vector3f(d[wpv * (i + initial) + offset],
+ d[wpv * (i + initial) + offset + 1],
+ d[wpv * (i + initial) + offset + 2]);
+ }
+ geomInfo.setNormals(normals);
+ offset += 3;
+ }
+
+ Point3f[] coords = new Point3f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ coords[i] = new Point3f(d[wpv * (i + initial) + offset],
+ d[wpv * (i + initial) + offset + 1],
+ d[wpv * (i + initial) + offset + 2]);
+ }
+ geomInfo.setCoordinates(coords);
+ } else {
+ // Data is not INTERLEAVED
+ boolean byRef = ((vertexFormat & GeometryArray.BY_REFERENCE) != 0 );
+ boolean nio = ((vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0 );
+
+ Point3f[] coords = null;
+ if (byRef) {
+
+ int initial;
+ if (!(geomArray instanceof IndexedGeometryArray)) {
+ initial = geomArray.getInitialCoordIndex();
+ } else initial = 0;
+
+ if ( nio ) {
+ J3DBuffer buf = geomArray.getCoordRefBuffer();
+
+ switch (BufferWrapper.getBufferType(buf)) {
+
+ case BufferWrapper.TYPE_FLOAT: {
+ FloatBufferWrapper bb = new FloatBufferWrapper(buf);
+ float[] c = new float[valid * 3];
+ bb.position(initial * 3);
+ bb.get(c, 0, valid * 3);
+ coords = new Point3f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ coords[i] = new Point3f(c[i * 3 + 0],
+ c[i * 3 + 1],
+ c[i * 3 + 2]);
+ }
+ }
+ break;
+
+ case BufferWrapper.TYPE_DOUBLE: {
+ DoubleBufferWrapper bb = new DoubleBufferWrapper( buf );
+ double[] c = new double[valid * 3];
+ bb.position(initial * 3);
+ bb.get(c, 0, valid * 3);
+ coords = new Point3f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ coords[i] = new Point3f((float)(c[i * 3 + 0]),
+ (float)(c[i * 3 + 1]),
+ (float)(c[i * 3 + 2]));
+ }
+ }
+ break;
+ }
+ } else if (geomArray.getCoordRef3f() != null) {
+ if (initial != 0) {
+ Point3f[] c = geomArray.getCoordRef3f();
+ coords = new Point3f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ coords[i] = new Point3f(c[i + initial]);
+ }
+ } else coords = geomArray.getCoordRef3f();
+ } else if (geomArray.getCoordRef3d() != null) {
+ Point3d[] c = geomArray.getCoordRef3d();
+ coords = new Point3f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ coords[i] = new Point3f(c[i + initial]);
+ }
+ } else if (geomArray.getCoordRefFloat() != null) {
+ float[] c = geomArray.getCoordRefFloat();
+ coords = new Point3f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ coords[i] = new Point3f(c[(i + initial) * 3],
+ c[(i + initial) * 3 + 1],
+ c[(i + initial) * 3 + 2]);
+ }
+ } else if (geomArray.getCoordRefDouble() != null) {
+ double[] c = geomArray.getCoordRefDouble();
+ coords = new Point3f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ coords[i] = new Point3f((float)(c[(i + initial) * 3]),
+ (float)(c[(i + initial) * 3 + 1]),
+ (float)(c[(i + initial) * 3 + 2]));
+ }
+ }
+ // No coordinate data - let GeometryInfo handle this.
+ } else {
+ // Not BY_REFERENCE
+ int initial;
+ if (!(geomArray instanceof IndexedGeometryArray)) {
+ initial = geomArray.getInitialVertexIndex();
+ } else initial = 0;
+ coords = new Point3f[valid];
+ for (i = 0 ; i < valid ; i++) coords[i] = new Point3f();
+ geomArray.getCoordinates(initial, coords);
+ }
+ geomInfo.setCoordinates(coords);
+
+ if ((vertexFormat & GeometryArray.NORMALS) != 0) {
+ Vector3f[] normals = null;
+ if (byRef) {
+
+ int initial;
+ if (!(geomArray instanceof IndexedGeometryArray)) {
+ initial = geomArray.getInitialNormalIndex();
+ } else initial = 0;
+
+ if ( nio ) {
+ J3DBuffer buf = geomArray.getNormalRefBuffer();
+
+ if (BufferWrapper.getBufferType(buf) == BufferWrapper.TYPE_FLOAT) {
+ FloatBufferWrapper bb = new FloatBufferWrapper(buf);
+ float[] c = new float[valid * 3];
+ bb.position(initial * 3);
+ bb.get(c, 0, valid * 3);
+ normals = new Vector3f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ normals[i] = new Vector3f(c[i * 3 + 0],
+ c[i * 3 + 1],
+ c[i * 3 + 2]);
+ }
+ }
+ // Normals were set in vertexFormat but none were set - OK
+ } else if (geomArray.getNormalRef3f() != null) {
+ if (initial != 0) {
+ Vector3f[] n = geomArray.getNormalRef3f();
+ normals = new Vector3f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ normals[i] = new Vector3f(n[i + initial]);
+ }
+ } else normals = geomArray.getNormalRef3f();
+ } else if (geomArray.getNormalRefFloat() != null) {
+ float[] n = geomArray.getNormalRefFloat();
+ normals = new Vector3f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ normals[i] = new Vector3f(n[(i + initial) * 3],
+ n[(i + initial) * 3 + 1],
+ n[(i + initial) * 3 + 2]);
+ }
+ }
+ // Normals were set in vertexFormat but none were set - OK
+ } else {
+ // Not BY_REFERENCE
+ int initial;
+ if (!(geomArray instanceof IndexedGeometryArray)) {
+ initial = geomArray.getInitialVertexIndex();
+ } else initial = 0;
+ normals = new Vector3f[valid];
+ for (i = 0 ; i < valid ; i++) normals[i] = new Vector3f();
+ geomArray.getNormals(initial, normals);
+ }
+ geomInfo.setNormals(normals);
+ }
+
+ if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4) {
+ Color4f[] colors = null;
+ if (byRef) {
+
+ int initial;
+ if (!(geomArray instanceof IndexedGeometryArray)) {
+ initial = geomArray.getInitialColorIndex();
+ } else initial = 0;
+
+ if ( nio ) {
+ J3DBuffer buf = geomArray.getColorRefBuffer();
+
+ switch (BufferWrapper.getBufferType(buf)) {
+
+ case BufferWrapper.TYPE_FLOAT: {
+ FloatBufferWrapper bb = new FloatBufferWrapper(buf);
+ float[] c = new float[valid * 4];
+ bb.position(initial * 4);
+ bb.get(c, 0, valid * 4);
+ colors = new Color4f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ colors[i] = new Color4f(c[i * 4 + 0],
+ c[i * 4 + 1],
+ c[i * 4 + 2],
+ c[i * 4 + 3]);
+ }
+ }
+ break;
+
+ case BufferWrapper.TYPE_BYTE: {
+ ByteBufferWrapper bb = new ByteBufferWrapper(buf);
+ byte[] c = new byte[valid * 4];
+ bb.position(initial * 4);
+ bb.get(c, 0, valid * 4);
+ colors = new Color4f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ colors[i] = new Color4f((float)(c[i * 4 + 0] & 0xff) / 255.0f,
+ (float)(c[i * 4 + 1] & 0xff) / 255.0f,
+ (float)(c[i * 4 + 2] & 0xff) / 255.0f,
+ (float)(c[i * 4 + 3] & 0xff) / 255.0f);
+ }
+ }
+ break;
+ }
+ } else if (geomArray.getColorRef4f() != null) {
+ if (initial != 0) {
+ Color4f[] c = geomArray.getColorRef4f();
+ colors = new Color4f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ colors[i] = new Color4f(c[i + initial]);
+ }
+ } else colors = geomArray.getColorRef4f();
+ } else if (geomArray.getColorRefFloat() != null) {
+ float[] c = geomArray.getColorRefFloat();
+ colors = new Color4f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ colors[i] = new Color4f(c[(i + initial) * 4 + 0],
+ c[(i + initial) * 4 + 1],
+ c[(i + initial) * 4 + 2],
+ c[(i + initial) * 4 + 3]);
+ }
+ } else if (geomArray.getColorRefByte() != null) {
+ byte[] c = geomArray.getColorRefByte();
+ colors = new Color4f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ colors[i] = new Color4f((float)(c[(i + initial) * 4 + 0] & 0xff) / 255.0f,
+ (float)(c[(i + initial) * 4 + 1] & 0xff) / 255.0f,
+ (float)(c[(i + initial) * 4 + 2] & 0xff) / 255.0f,
+ (float)(c[(i + initial) * 4 + 3] & 0xff) / 255.0f);
+ }
+ } else if (geomArray.getColorRef4b() != null) {
+ Color4b[] c = geomArray.getColorRef4b();
+ colors = new Color4f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ colors[i] = new Color4f((float)(c[i + initial].x & 0xff) / 255.0f,
+ (float)(c[i + initial].y & 0xff) / 255.0f,
+ (float)(c[i + initial].z & 0xff) / 255.0f,
+ (float)(c[i + initial].w & 0xff) / 255.0f);
+ }
+ }
+ // Colors4 were set in vertexFormat but none were set - OK
+ } else {
+ // Not BY_REFERENCE
+ int initial;
+ if (!(geomArray instanceof IndexedGeometryArray)) {
+ initial = geomArray.getInitialVertexIndex();
+ } else initial = 0;
+ colors = new Color4f[valid];
+ for (i = 0 ; i < valid ; i++) colors[i] = new Color4f();
+ geomArray.getColors(initial, colors);
+ }
+ geomInfo.setColors(colors);
+ } else if ((vertexFormat & GeometryArray.COLOR_3) != 0) {
+ Color3f[] colors = null;
+ if (byRef) {
+
+ int initial;
+ if (!(geomArray instanceof IndexedGeometryArray)) {
+ initial = geomArray.getInitialColorIndex();
+ } else initial = 0;
+
+ if ( nio ) {
+ J3DBuffer buf = geomArray.getColorRefBuffer();
+
+ switch (BufferWrapper.getBufferType(buf)) {
+
+ case BufferWrapper.TYPE_FLOAT: {
+ FloatBufferWrapper bb = new FloatBufferWrapper(buf);
+ float[] c = new float[valid * 3];
+ bb.position(initial * 3);
+ bb.get(c, 0, valid * 3);
+ colors = new Color3f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ colors[i] = new Color3f(c[i * 3 + 0],
+ c[i * 3 + 1],
+ c[i * 3 + 2]);
+ }
+ }
+ break;
+
+ case BufferWrapper.TYPE_BYTE: {
+ ByteBufferWrapper bb = new ByteBufferWrapper(buf);
+ byte[] c = new byte[valid * 3];
+ bb.position(initial * 3);
+ bb.get(c, 0, valid * 3);
+ colors = new Color3f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ colors[i] = new Color3f((float)(c[i * 3 + 0] & 0xff) / 255.0f,
+ (float)(c[i * 3 + 1] & 0xff) / 255.0f,
+ (float)(c[i * 3 + 2] & 0xff) / 255.0f);
+ }
+ }
+ break;
+ }
+ } else if (geomArray.getColorRef3f() != null) {
+ if (initial != 0) {
+ Color3f[] c = geomArray.getColorRef3f();
+ colors = new Color3f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ colors[i] = new Color3f(c[i + initial]);
+ }
+ } else colors = geomArray.getColorRef3f();
+ } else if (geomArray.getColorRefFloat() != null) {
+ float[] c = geomArray.getColorRefFloat();
+ colors = new Color3f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ colors[i] = new Color3f(c[(i + initial) * 3 + 0],
+ c[(i + initial) * 3 + 1],
+ c[(i + initial) * 3 + 2]);
+ }
+ } else if (geomArray.getColorRefByte() != null) {
+ byte[] c = geomArray.getColorRefByte();
+ colors = new Color3f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ colors[i] = new Color3f((float)(c[(i + initial) * 3 + 0] & 0xff) / 255.0f,
+ (float)(c[(i + initial) * 3 + 1] & 0xff) / 255.0f,
+ (float)(c[(i + initial) * 3 + 2] & 0xff) / 255.0f);
+ }
+ } else if (geomArray.getColorRef3b() != null) {
+ Color3b[] c = geomArray.getColorRef3b();
+ colors = new Color3f[valid];
+ for (i = 0 ; i < valid ; i++) {
+ colors[i] = new Color3f((float)(c[i + initial].x & 0xff) / 255.0f,
+ (float)(c[i + initial].y & 0xff) / 255.0f,
+ (float)(c[i + initial].z & 0xff) / 255.0f);
+ }
+ }
+ // Colors3 were set in vertexFormat but none were set - OK
+ } else {
+ // Not BY_REFERENCE
+ int initial;
+ if (!(geomArray instanceof IndexedGeometryArray)) {
+ initial = geomArray.getInitialVertexIndex();
+ } else initial = 0;
+ colors = new Color3f[valid];
+ for (i = 0 ; i < valid ; i++) colors[i] = new Color3f();
+ geomArray.getColors(initial, colors);
+ }
+ geomInfo.setColors(colors);
+ }
+
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0) {
+ geomInfo.setTextureCoordinateParams(texSets, 4);
+ for (i = 0 ; i < texSets ; i++) {
+ TexCoord4f[] tex = null;
+ if (byRef) {
+
+ int initial;
+ if (!(geomArray instanceof IndexedGeometryArray)) {
+ initial = geomArray.getInitialTexCoordIndex(i);
+ } else initial = 0;
+
+ if (nio) {
+ J3DBuffer buf = geomArray.getTexCoordRefBuffer(i);
+
+ if (BufferWrapper.getBufferType(buf) == BufferWrapper.TYPE_FLOAT) {
+ FloatBufferWrapper bb = new FloatBufferWrapper(buf);
+ float[] c = new float[valid * 4];
+ bb.position(initial * 4);
+ bb.get(c, 0, valid * 4);
+ tex = new TexCoord4f[valid];
+ for (j = 0 ; j < valid ; j++) {
+ tex[j] = new TexCoord4f(c[j * 4 + 0],
+ c[j * 4 + 1],
+ c[j * 4 + 2],
+ c[j * 4 + 3]);
+ }
+ }
+ // TexCoords4 were set in vertexFormat but none were set - OK
+ } else {
+ // There if no TexCoordRef4f, so we know it's float
+ float[] t = geomArray.getTexCoordRefFloat(i);
+ tex = new TexCoord4f[valid];
+ for (j = 0 ; j < valid ; j++) {
+ tex[j] = new TexCoord4f(t[(j + initial) * 4],
+ t[(j + initial) * 4 + 1],
+ t[(j + initial) * 4 + 2],
+ t[(j + initial) * 4 + 3]);
+ }
+ }
+ } else {
+ // Not BY_REFERENCE
+ int initial;
+ if (!(geomArray instanceof IndexedGeometryArray)) {
+ initial = geomArray.getInitialVertexIndex();
+ } else initial = 0;
+ tex = new TexCoord4f[valid];
+ for (j = 0 ; j < valid ; j++) tex[j] = new TexCoord4f();
+ geomArray.getTextureCoordinates(i, initial, tex);
+ }
+ geomInfo.setTextureCoordinates(i, tex);
+ }
+ int[] map = new int[geomArray.getTexCoordSetMapLength()];
+ geomArray.getTexCoordSetMap(map);
+ geomInfo.setTexCoordSetMap(map);
+ } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) {
+ geomInfo.setTextureCoordinateParams(texSets, 3);
+ for (i = 0 ; i < texSets ; i++) {
+ TexCoord3f[] tex = null;
+ if (byRef) {
+
+ int initial;
+ if (!(geomArray instanceof IndexedGeometryArray)) {
+ initial = geomArray.getInitialTexCoordIndex(i);
+ } else initial = 0;
+
+ if (nio) {
+ J3DBuffer buf = geomArray.getTexCoordRefBuffer(i);
+
+ if (BufferWrapper.getBufferType(buf) == BufferWrapper.TYPE_FLOAT) {
+ FloatBufferWrapper bb = new FloatBufferWrapper(buf);
+ float[] c = new float[valid * 3];
+ bb.position(initial * 3);
+ bb.get(c, 0, valid * 3);
+ tex = new TexCoord3f[valid];
+ for (j = 0 ; j < valid ; j++) {
+ tex[j] = new TexCoord3f(c[j * 3 + 0],
+ c[j * 3 + 1],
+ c[j * 3 + 2]);
+ }
+ }
+ // TexCoords3 were set in vertexFormat but none were set - OK
+ } else if (geomArray.getTexCoordRef3f(i) != null) {
+ if (initial != 0) {
+ TexCoord3f[] t = geomArray.getTexCoordRef3f(i);
+ tex = new TexCoord3f[valid];
+ for (j = 0 ; j < valid ; j++) {
+ tex[j] = new TexCoord3f(t[j + initial]);
+ }
+ } else tex = geomArray.getTexCoordRef3f(i);
+ } else if (geomArray.getTexCoordRefFloat(i) != null) {
+ float[] t = geomArray.getTexCoordRefFloat(i);
+ tex = new TexCoord3f[valid];
+ for (j = 0 ; j < valid ; j++) {
+ tex[j] = new TexCoord3f(t[(j + initial) * 3],
+ t[(j + initial) * 3 + 1],
+ t[(j + initial) * 3 + 2]);
+ }
+ }
+ // TexCoords3 were set in vertexFormat but none were set - OK
+ } else {
+ // Not BY_REFERENCE
+ int initial;
+ if (!(geomArray instanceof IndexedGeometryArray)) {
+ initial = geomArray.getInitialVertexIndex();
+ } else initial = 0;
+ tex = new TexCoord3f[valid];
+ for (j = 0 ; j < valid ; j++) tex[j] = new TexCoord3f();
+ geomArray.getTextureCoordinates(i, initial, tex);
+ }
+ geomInfo.setTextureCoordinates(i, tex);
+ }
+ int[] map = new int[geomArray.getTexCoordSetMapLength()];
+ geomArray.getTexCoordSetMap(map);
+ geomInfo.setTexCoordSetMap(map);
+ } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0 ) {
+ geomInfo.setTextureCoordinateParams(texSets, 2);
+ for (i = 0 ; i < texSets ; i++) {
+ TexCoord2f[] tex = null;
+ if (byRef) {
+
+ int initial;
+ if (!(geomArray instanceof IndexedGeometryArray)) {
+ initial = geomArray.getInitialTexCoordIndex(i);
+ } else initial = 0;
+
+ if (nio) {
+ J3DBuffer buf = geomArray.getTexCoordRefBuffer(i);
+
+ if (BufferWrapper.getBufferType(buf) == BufferWrapper.TYPE_FLOAT) {
+ FloatBufferWrapper bb = new FloatBufferWrapper(buf);
+ float[] c = new float[valid * 2];
+ bb.position(initial * 2);
+ bb.get(c, 0, valid * 2);
+ tex = new TexCoord2f[valid];
+ for (j = 0 ; j < valid ; j++) {
+ tex[j] = new TexCoord2f(c[j * 2 + 0],
+ c[j * 2 + 1]);
+ }
+ }
+ // TexCoords2 were set in vertexFormat but none were set - OK
+ } else if (geomArray.getTexCoordRefFloat(i) != null) {
+ float[] t = geomArray.getTexCoordRefFloat(i);
+ tex = new TexCoord2f[valid];
+ for (j = 0 ; j < valid ; j++) {
+ tex[j] = new TexCoord2f(t[(j + initial) * 2 + 0],
+ t[(j + initial) * 2 + 1]);
+ }
+ } else if (geomArray.getTexCoordRef2f(i) != null) {
+ if (initial != 0) {
+ TexCoord2f[] t = geomArray.getTexCoordRef2f(i);
+ tex = new TexCoord2f[valid];
+ for (j = 0 ; j < valid ; j++) {
+ tex[j] = new TexCoord2f(t[j + initial]);
+ }
+ } else tex = geomArray.getTexCoordRef2f(i);
+ }
+ // TexCoords2 were set in vertexFormat but none were set - OK
+ } else {
+ // Not BY_REFERENCE
+ int initial;
+ if (!(geomArray instanceof IndexedGeometryArray)) {
+ initial = geomArray.getInitialVertexIndex();
+ } else initial = 0;
+ tex = new TexCoord2f[valid];
+ for (j = 0 ; j < valid ; j++) tex[j] = new TexCoord2f();
+ geomArray.getTextureCoordinates(i, initial, tex);
+ }
+ geomInfo.setTextureCoordinates(i, tex);
+ }
+ int[] map = new int[geomArray.getTexCoordSetMapLength()];
+ geomArray.getTexCoordSetMap(map);
+ geomInfo.setTexCoordSetMap(map);
+ }
+ }
+ } // End of processGeometryArray
+
+
+
+ private static void processIndexedArray(GeometryInfo geomInfo,
+ IndexedGeometryArray geomArray)
+ {
+ int initial = geomArray.getInitialIndexIndex();
+ int vertexFormat = geomArray.getVertexFormat();
+ int texSets = geomArray.getTexCoordSetCount();
+
+ int valid;
+ if (geomArray instanceof IndexedGeometryStripArray) {
+ IndexedGeometryStripArray igsa = (IndexedGeometryStripArray)geomArray;
+ int[] strips = new int[igsa.getNumStrips()];
+ igsa.getStripIndexCounts(strips);
+ valid = 0;
+ for (int i = 0 ; i < strips.length ; i++) {
+ valid += strips[i];
+ }
+ } else {
+ valid = geomArray.getValidIndexCount();
+ }
+
+ int[] coordI = new int[valid];
+ geomArray.getCoordinateIndices(initial, coordI);
+ geomInfo.setCoordinateIndices(coordI);
+
+ if ((vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0) {
+ if ((vertexFormat & GeometryArray.NORMALS) != 0)
+ geomInfo.setNormalIndices(coordI);
+ if (((vertexFormat & GeometryArray.COLOR_3) != 0) ||
+ ((vertexFormat & GeometryArray.COLOR_4) != 0))
+ geomInfo.setColorIndices(coordI);
+ if (((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) ||
+ ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) ||
+ ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0)) {
+ for (int i = 0 ; i < texSets ; i++) {
+ geomInfo.setTextureCoordinateIndices(i, coordI);
+ }
+ }
+ } else {
+ if ((vertexFormat & GeometryArray.NORMALS) != 0) {
+ int[] normalI = new int[valid];
+ geomArray.getNormalIndices(initial, normalI);
+ geomInfo.setNormalIndices(normalI);
+ }
+
+ if (((vertexFormat & GeometryArray.COLOR_3) != 0) ||
+ ((vertexFormat & GeometryArray.COLOR_4) != 0)) {
+ int[] colorI = new int[valid];
+ geomArray.getColorIndices(initial, colorI);
+ geomInfo.setColorIndices(colorI);
+ }
+
+ if (((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) ||
+ ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) ||
+ ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0)) {
+ for (int i = 0 ; i < texSets ; i++) {
+ int[] texI = new int[valid];
+ geomArray.getTextureCoordinateIndices(i, initial, texI);
+ geomInfo.setTextureCoordinateIndices(i, texI);
+ }
+ }
+ }
+ } // End of processIndexedArray
+
+
+
+ private static void processStripArray(GeometryInfo geomInfo,
+ GeometryStripArray geomArray)
+ {
+ int[] strips = new int[geomArray.getNumStrips()];
+ geomArray.getStripVertexCounts(strips);
+ geomInfo.setStripCounts(strips);
+ } // End of processStripArray
+
+
+
+ private static void processIndexStripArray(
+ GeometryInfo geomInfo, IndexedGeometryStripArray geomArray)
+ {
+ int[] strips = new int[geomArray.getNumStrips()];
+ geomArray.getStripIndexCounts(strips);
+ geomInfo.setStripCounts(strips);
+ } // End of processIndexStripArray
+
+} // End of class GeometryInfoGenerator
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Heap.java b/src/classes/share/com/sun/j3d/utils/geometry/Heap.java
new file mode 100644
index 0000000..8ae7735
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Heap.java
@@ -0,0 +1,212 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+import java.io.*;
+import java.util.*;
+import javax.vecmath.*;
+
+class Heap {
+
+ static void printHeapData(Triangulator triRef) {
+ int i;
+ System.out.println("\nHeap Data : numZero " + triRef.numZero +
+ " numHeap " + triRef.numHeap);
+ for(i=0; i< triRef.numHeap; i++)
+ System.out.println(i + " ratio " + triRef.heap[i].ratio + ", index " +
+ triRef.heap[i].index + ", prev " +
+ triRef.heap[i].prev + ", next " +
+ triRef.heap[i].next);
+
+ System.out.println(" ");
+
+
+ }
+
+ static void initHeap(Triangulator triRef) {
+ // Calculate the maximum bounds : N + (N -2)* 2.
+ // triRef.maxNumHeap = triRef.numPoints * 3;
+ triRef.maxNumHeap = triRef.numPoints;
+ triRef.heap = new HeapNode[triRef.maxNumHeap];
+
+ triRef.numHeap = 0;
+ triRef.numZero = 0;
+
+ }
+
+ static void storeHeapData(Triangulator triRef, int index, double ratio,
+ int ind, int prev, int next) {
+ triRef.heap[index] = new HeapNode();
+ triRef.heap[index].ratio = ratio;
+ triRef.heap[index].index = ind;
+ triRef.heap[index].prev = prev;
+ triRef.heap[index].next = next;
+ }
+
+ static void dumpOnHeap(Triangulator triRef, double ratio,
+ int ind, int prev, int next) {
+ int index;
+
+ if (triRef.numHeap >= triRef.maxNumHeap) {
+ // System.out.println("Heap:dumpOnHeap.Expanding heap array ...");
+ HeapNode old[] = triRef.heap;
+ triRef.maxNumHeap = triRef.maxNumHeap + triRef.numPoints;
+ triRef.heap = new HeapNode[triRef.maxNumHeap];
+ System.arraycopy(old, 0, triRef.heap, 0, old.length);
+ }
+ if (ratio == 0.0) {
+ if (triRef.numZero < triRef.numHeap)
+ if(triRef.heap[triRef.numHeap] == null)
+ storeHeapData(triRef, triRef.numHeap, triRef.heap[triRef.numZero].ratio,
+ triRef.heap[triRef.numZero].index,
+ triRef.heap[triRef.numZero].prev,
+ triRef.heap[triRef.numZero].next);
+ else
+ triRef.heap[triRef.numHeap].copy(triRef.heap[triRef.numZero]);
+ /*
+ storeHeapData(triRef, triRef.numHeap, triRef.heap[triRef.numZero].ratio,
+ triRef.heap[triRef.numZero].index,
+ triRef.heap[triRef.numZero].prev,
+ triRef.heap[triRef.numZero].next);
+ */
+ index = triRef.numZero;
+ ++triRef.numZero;
+ }
+ else {
+ index = triRef.numHeap;
+ }
+
+ storeHeapData(triRef, index, ratio, ind, prev, next);
+ ++triRef.numHeap;
+
+ }
+
+
+ static void insertIntoHeap(Triangulator triRef, double ratio,
+ int ind, int prev, int next) {
+ dumpOnHeap(triRef, ratio, ind, prev, next);
+ }
+
+
+ static boolean deleteFromHeap(Triangulator triRef, int[] ind,
+ int[] prev, int[] next) {
+ double rnd;
+ int rndInd;
+
+ // earSorted is not implemented yet.
+
+ if (triRef.numZero > 0) {
+ // assert(num_heap >= num_zero);
+ --triRef.numZero;
+ --triRef.numHeap;
+
+ ind[0] = triRef.heap[triRef.numZero].index;
+ prev[0] = triRef.heap[triRef.numZero].prev;
+ next[0] = triRef.heap[triRef.numZero].next;
+ if (triRef.numZero < triRef.numHeap)
+ triRef.heap[triRef.numZero].copy(triRef.heap[triRef.numHeap]);
+ /*
+ storeHeapData( triRef, triRef.numZero, triRef.heap[triRef.numHeap].ratio,
+ triRef.heap[triRef.numHeap].index,
+ triRef.heap[triRef.numHeap].prev,
+ triRef.heap[triRef.numHeap].next);
+ */
+ return true;
+ }
+ else if (triRef.earsRandom) {
+ if (triRef.numHeap <= 0) {
+ triRef.numHeap = 0;
+ return false;
+ }
+ rnd = triRef.randomGen.nextDouble();
+ rndInd = (int)(rnd * triRef.numHeap);
+ --triRef.numHeap;
+ if (rndInd > triRef.numHeap) rndInd = triRef.numHeap;
+
+ ind[0] = triRef.heap[rndInd].index;
+ prev[0] = triRef.heap[rndInd].prev;
+ next[0] = triRef.heap[rndInd].next;
+ if (rndInd < triRef.numHeap)
+ triRef.heap[rndInd].copy(triRef.heap[triRef.numHeap]);
+ /*
+ storeHeapData( triRef, rndInd, triRef.heap[triRef.numHeap].ratio,
+ triRef.heap[triRef.numHeap].index,
+ triRef.heap[triRef.numHeap].prev,
+ triRef.heap[triRef.numHeap].next);
+ */
+ return true;
+ }
+ else {
+ if (triRef.numHeap <= 0) {
+ triRef.numHeap = 0;
+ return false;
+ }
+ --triRef.numHeap;
+ ind[0] = triRef.heap[triRef.numHeap].index;
+ prev[0] = triRef.heap[triRef.numHeap].prev;
+ next[0] = triRef.heap[triRef.numHeap].next;
+
+ return true;
+ }
+
+ // return false;
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/HeapNode.java b/src/classes/share/com/sun/j3d/utils/geometry/HeapNode.java
new file mode 100644
index 0000000..37f02c0
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/HeapNode.java
@@ -0,0 +1,75 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+class HeapNode {
+ int index, prev, next;
+ double ratio;
+
+ HeapNode() {
+ }
+
+ void copy(HeapNode hNode) {
+ index = hNode.index;
+ prev = hNode.prev;
+ next = hNode.next;
+ ratio = hNode.ratio;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Left.java b/src/classes/share/com/sun/j3d/utils/geometry/Left.java
new file mode 100644
index 0000000..72a66dd
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Left.java
@@ -0,0 +1,74 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+class Left extends Object {
+ int ind;
+ int index;
+
+ Left() {
+ }
+
+ void copy(Left l) {
+ ind = l.ind;
+ index = l.index;
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/ListNode.java b/src/classes/share/com/sun/j3d/utils/geometry/ListNode.java
new file mode 100644
index 0000000..15b8983
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/ListNode.java
@@ -0,0 +1,87 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+class ListNode {
+ int index;
+ int prev;
+ int next;
+ int convex;
+ int vcntIndex; // Vertex, Color, Normal, Texture Index
+
+
+
+ ListNode(int ind) {
+ index = ind;
+ prev = -1;
+ next = -1;
+ convex = 0;
+ vcntIndex = -1;
+ }
+
+ void setCommonIndex(int comIndex) {
+ vcntIndex = comIndex;
+
+ }
+
+ int getCommonIndex() {
+ return vcntIndex;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/NoHash.java b/src/classes/share/com/sun/j3d/utils/geometry/NoHash.java
new file mode 100644
index 0000000..35e25b5
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/NoHash.java
@@ -0,0 +1,302 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+import java.io.*;
+import java.util.*;
+import javax.vecmath.*;
+
+class NoHash {
+
+ static final int NIL = -1;
+
+
+ static void insertAfterVtx(Triangulator triRef, int iVtx) {
+ int size;
+
+ if (triRef.vtxList == null) {
+ size = Math.max(triRef.numVtxList+1, 100);
+ triRef.vtxList = new PntNode[size];
+ } else if (triRef.numVtxList >= triRef.vtxList.length) {
+ size = Math.max(triRef.numVtxList+1,
+ triRef.vtxList.length + 100);
+ PntNode old[] = triRef.vtxList;
+ triRef.vtxList = new PntNode[size];
+ System.arraycopy(old, 0, triRef.vtxList, 0, old.length);
+ }
+
+ triRef.vtxList[triRef.numVtxList] = new PntNode();
+ triRef.vtxList[triRef.numVtxList].pnt = iVtx;
+ triRef.vtxList[triRef.numVtxList].next = triRef.reflexVertices;
+ triRef.reflexVertices = triRef.numVtxList;
+ ++triRef.numVtxList;
+ ++triRef.numReflex;
+ }
+
+ static void deleteFromList(Triangulator triRef, int i) {
+ int indPnt, indPnt1;
+ int indVtx;
+
+ if(triRef.numReflex == 0) {
+ // System.out.println("NoHash:deleteFromList. numReflex is 0.");
+ return;
+
+ }
+ indPnt = triRef.reflexVertices;
+ if(inVtxList(triRef, indPnt)==false)
+ System.out.println("NoHash:deleteFromList. Problem :Not is InVtxList ..." +
+ indPnt);
+
+ indVtx = triRef.vtxList[indPnt].pnt;
+
+ if (indVtx == i) {
+ triRef.reflexVertices = triRef.vtxList[indPnt].next;
+ --triRef.numReflex;
+ }
+ else {
+ indPnt1 = triRef.vtxList[indPnt].next;
+ while (indPnt1 != NIL) {
+ if(inVtxList(triRef, indPnt1)==false)
+ System.out.println("NoHash:deleteFromList. Problem :Not is InVtxList ..."+
+ indPnt1);
+
+ indVtx = triRef.vtxList[indPnt1].pnt;
+ if (indVtx == i) {
+ triRef.vtxList[indPnt].next = triRef.vtxList[indPnt1].next;
+ indPnt1 = NIL;
+ --triRef.numReflex;
+ }
+ else {
+ indPnt = indPnt1;
+ indPnt1 = triRef.vtxList[indPnt].next;
+ }
+ }
+ }
+ }
+
+
+ static boolean inVtxList(Triangulator triRef, int vtx) {
+ return ((0 <= vtx) && (vtx < triRef.numVtxList));
+ }
+
+
+ static void freeNoHash(Triangulator triRef) {
+
+ triRef.noHashingEdges = false;
+ triRef.noHashingPnts = false;
+
+ triRef.numVtxList = 0;
+ }
+
+
+
+ static void prepareNoHashEdges(Triangulator triRef,
+ int currLoopMin, int currLoopMax) {
+ triRef.loopMin = currLoopMin;
+ triRef.loopMax = currLoopMax;
+
+ triRef.noHashingEdges = true;
+
+ return;
+ }
+
+
+ static void prepareNoHashPnts(Triangulator triRef, int currLoopMin) {
+ int ind, ind1;
+ int i1;
+
+ triRef.numVtxList = 0;
+ triRef.reflexVertices = NIL;
+
+ // insert the reflex vertices into a list
+ ind = triRef.loops[currLoopMin];
+ ind1 = ind;
+ triRef.numReflex = 0;
+ i1 = triRef.fetchData(ind1);
+
+ do {
+ if (triRef.getAngle(ind1) < 0)
+ insertAfterVtx(triRef, ind1);
+
+ ind1 = triRef.fetchNextData(ind1);
+ i1 = triRef.fetchData(ind1);
+ } while (ind1 != ind);
+
+ triRef.noHashingPnts = true;
+
+ }
+
+
+ static boolean noHashIntersectionExists(Triangulator triRef, int i1, int ind1,
+ int i2, int i3, BBox bb) {
+ int indVtx, ind5;
+ int indPnt;
+ int i4, i5;
+ int type[] = new int[1];
+ boolean flag;
+ double y;
+
+ if(triRef.noHashingPnts==false)
+ System.out.println("NoHash:noHashIntersectionExists noHashingPnts is false");
+
+ // assert(InPointsList(i1));
+ // assert(InPointsList(i2));
+ // assert(InPointsList(i3));
+
+ if (triRef.numReflex <= 0) return false;
+
+ // first, let's extend the BBox of the line segment i2, i3 to a BBox
+ // of the entire triangle.
+ if (i1 < bb.imin) bb.imin = i1;
+ else if (i1 > bb.imax) bb.imax = i1;
+ y = triRef.points[i1].y;
+ if (y < bb.ymin) bb.ymin = y;
+ else if (y > bb.ymax) bb.ymax = y;
+
+ // check whether the triangle i1, i2, i3 contains any reflex vertex; we
+ // assume that i2, i3 is the new diagonal, and that the triangle is
+ // oriented CCW.
+ indPnt = triRef.reflexVertices;
+ flag = false;
+ do {
+ // assert(InVtxList(ind_pnt));
+ indVtx = triRef.vtxList[indPnt].pnt;
+ // assert(InPolyList(ind_vtx));
+ i4 = triRef.fetchData(indVtx);
+
+
+ if (bb.pntInBBox(triRef, i4)) {
+ // only if the reflex vertex lies inside the BBox of the triangle.
+ ind5 = triRef.fetchNextData(indVtx);
+ i5 = triRef.fetchData(ind5);
+ if ((indVtx != ind1) && (indVtx != ind5)) {
+ // only if this node isn't i1, and if it still belongs to the
+ // polygon
+ if (i4 == i1) {
+ if (Degenerate.handleDegeneracies(triRef, i1, ind1, i2, i3, i4, indVtx))
+ return true;
+ }
+ else if ((i4 != i2) && (i4 != i3)) {
+ flag = Numerics.vtxInTriangle(triRef, i1, i2, i3, i4, type);
+ if (flag) return true;
+ }
+ }
+ }
+ indPnt = triRef.vtxList[indPnt].next;
+
+ } while (indPnt != NIL);
+
+ return false;
+ }
+
+
+
+
+ static void deleteReflexVertex(Triangulator triRef, int ind) {
+ // assert(InPolyList(ind));
+ deleteFromList(triRef, ind);
+ }
+
+
+
+
+ static boolean noHashEdgeIntersectionExists(Triangulator triRef, BBox bb, int i1,
+ int i2, int ind5, int i5) {
+ int ind, ind2;
+ int i, i3, i4;
+ BBox bb1;
+
+ if(triRef.noHashingEdges==false)
+ System.out.println("NoHash:noHashEdgeIntersectionExists noHashingEdges is false");
+
+ triRef.identCntr = 0;
+
+ // check the boundary segments.
+ for (i = triRef.loopMin; i < triRef.loopMax; ++i) {
+ ind = triRef.loops[i];
+ ind2 = ind;
+ i3 = triRef.fetchData(ind2);
+
+ do {
+ ind2 = triRef.fetchNextData(ind2);
+ i4 = triRef.fetchData(ind2);
+ // check this segment. we first compute its bounding box.
+ bb1 = new BBox(triRef, i3, i4);
+ if (bb.BBoxOverlap(bb1)) {
+ if (Numerics.segIntersect(triRef, bb.imin, bb.imax, bb1.imin, bb1.imax, i5))
+ return true;
+ }
+ i3 = i4;
+ } while (ind2 != ind);
+ }
+
+ // oops! this segment shares one endpoint with at least four other
+ // boundary segments! oh well, yet another degenerate situation...
+ if (triRef.identCntr >= 4) {
+ if (BottleNeck.checkBottleNeck(triRef, i5, i1, i2, ind5))
+ return true;
+ else
+ return false;
+ }
+
+ return false;
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/NormalGenerator.java b/src/classes/share/com/sun/j3d/utils/geometry/NormalGenerator.java
new file mode 100644
index 0000000..803a5c5
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/NormalGenerator.java
@@ -0,0 +1,907 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry;
+
+import com.sun.j3d.utils.geometry.GeometryInfo;
+import com.sun.j3d.utils.geometry.EdgeTable;
+import java.util.ArrayList;
+import javax.vecmath.Vector3f;
+import javax.vecmath.Point3f;
+
+/**
+ * The NormalGenerator utility will calculate and fill in the normals
+ * of a GeometryInfo object. The calculated normals are estimated based
+ * on an analysis of the indexed coordinate information. If your data
+ * isn't indexed, index lists will be created.<p>
+ * <p>
+ * If two (or more) triangles in the model share the same coordinate
+ * index then the normal generator will attempt to generate one normal
+ * for the vertex, resulting in a "smooth" looking surface. If two
+ * coordinates don't have the same index then they will have two
+ * separate normals, even if they have the same position. This will
+ * result in a "crease" in your object. If you suspect that your
+ * data isn't properly indexed, call GeometryInfo.recomputeIndexes(). <p>
+ * <p>
+ * Of course, sometimes your model *has* a crease in it. That's what
+ * creaseAngle is. If two triangles' normals differ by more than
+ * creaseAngle, then the vertex will get two separate normals, creating a
+ * discontinuous crease in the model. This is perfect for the edge
+ * of a table or the corner of a cube, for instance.
+ */
+
+public class NormalGenerator {
+
+ private double creaseAngle;
+ private Vector3f facetNorms[];
+ private ArrayList tally;
+ private GeometryInfo gi;
+ private int coordInds[];
+ private int normalInds[];
+ private int colorInds[];
+ private int texInds[][];
+ private int stripCounts[];
+ private static long t1=0, t2=0, t3=0, t4=0, t5=0, t6=0;
+ private Triangulator tr = null;
+ private int numTexSets;
+
+
+ // 0 - No debug info
+ // 1 - Facet Normals
+ // 2 - Connection info
+ // 4 - Normals
+ // 8 - StripCounts
+ // 16 - Timing
+ // 32 - Hard edges
+ // 64 - Coordinate and normal indices
+ // 128 - Vertex normal calculation info
+ private static final int DEBUG = 0;
+
+
+
+ // Calculate the normal of each triangle in the list by finding
+ // the cross product
+ private void calculatefacetNorms()
+ {
+ Point3f coordinates[] = gi.getCoordinates();
+ facetNorms = new Vector3f[coordInds.length / 3];
+ Vector3f a = new Vector3f();
+ Vector3f b = new Vector3f();
+ if ((DEBUG & 1) != 0) System.out.println("Facet normals:");
+
+ if (gi.getOldPrim() != gi.QUAD_ARRAY) {
+ for (int t = 0 ; t < coordInds.length ; t += 3) {
+ a.sub(coordinates[coordInds[t + 2]], coordinates[coordInds[t + 1]]);
+ b.sub(coordinates[coordInds[t + 0]], coordinates[coordInds[t + 1]]);
+ facetNorms[t / 3] = new Vector3f();
+ facetNorms[t / 3].cross(a, b);
+ facetNorms[t / 3].normalize();
+
+ if (Float.isNaN(facetNorms[t / 3].x)) {
+ // Normal isn't valid
+ facetNorms[t / 3].x = 1.0f;
+ facetNorms[t / 3].y = facetNorms[t / 3].z = 0.0f;
+ }
+ if ((DEBUG & 1) != 0) {
+ System.out.println(" " + (t/3) + " " + facetNorms[t / 3]);
+ }
+ }
+ } else {
+ // For quads, the facet normal of both triangles is the cross
+ // product of the two vectors that make an 'X' across the quad.
+ for (int t = 0 ; t < coordInds.length ; t += 6) {
+ a.sub(coordinates[coordInds[t + 2]], coordinates[coordInds[t + 0]]);
+ b.sub(coordinates[coordInds[t + 5]], coordinates[coordInds[t + 1]]);
+ facetNorms[t / 3] = new Vector3f();
+ facetNorms[t / 3].cross(a, b);
+ facetNorms[t / 3].normalize();
+
+ if (Float.isNaN(facetNorms[t / 3].x)) {
+ // Normal isn't valid
+ facetNorms[t / 3].x = 1.0f;
+ facetNorms[t / 3].y = facetNorms[t / 3].z = 0.0f;
+ }
+
+ // Second triangle of quad
+ facetNorms[t / 3 + 1] = new Vector3f(facetNorms[t / 3]);
+
+ if ((DEBUG & 1) != 0) {
+ System.out.println(" " + (t/3) + "&" + (t/3 + 1) + " " +
+ facetNorms[t / 3]);
+ }
+ }
+ }
+ } // End of calculatefacetNorms
+
+
+
+ // The vertex normals will be calculated by averaging the facet normals
+ // of groups of triangles sharing the vertex. At the end of this routine
+ // the groups of coordinate indexes will all be made, and the normal
+ // indices will point to these groups.
+ //
+ // The routine works by going through each vertex of each triangle.
+ // Starting at a triangle, we see if the vertex normal can be shared
+ // with the neighbor triangle (their facet normals differ by less than
+ // creaseAngle). If they can be shared, then we move from that triangle
+ // to the next and the next in a circle around the vertex.
+ //
+ // If we hit the edge of the model or a Hard Edge (crease) then we stop
+ // and then try going around the vertex in the other direction.
+ //
+ // Each time we step from one triangle to the next around the center
+ // vertex, the triangle is added to the group of triangles whose normals
+ // will be averaged to make the vertex normal.
+ //
+ // Returns the largest number of triangles that share a single normal.
+ //
+ private int createHardEdges()
+ {
+ EdgeTable et = new EdgeTable(coordInds);
+ tally = new ArrayList();
+ int normalMap[] = new int[coordInds.length];
+ int maxShare = 1;
+ float cosine;
+ boolean smooth;
+ float threshold = (float)Math.cos(creaseAngle);
+ boolean goingRight;
+
+ // Set Normal Indices array values to a flag
+ for (int c = 0 ; c < coordInds.length ; c++)
+ normalMap[c] = Integer.MAX_VALUE;
+
+ // Cycle through each vertex
+ for (int c = 0 ; c < coordInds.length ; c++) {
+ // See if this vertex's normal has already been done
+ if (normalMap[c] == Integer.MAX_VALUE) {
+ if ((DEBUG & 32) != 0) {
+ System.out.println(
+ "Coordinate Index " + c + ": vertex " + coordInds[c]);
+ }
+ // Create a list of vertices used for calculating this normal
+ ArrayList sharers = new ArrayList();
+ tally.add(sharers);
+ // Put this coordinate in the list
+ sharers.add(new Integer(c));
+ // Point this coordinate's index at its list
+ normalMap[c] = tally.size() - 1;
+
+ // First do right edge
+ goingRight = true;
+ Edge edge = new Edge(coordInds[c],
+ coordInds[(c + 1) % 3 == 0 ? c - 2 : c + 1]);
+ if ((DEBUG & 32) != 0)
+ System.out.println( " Right edge: " + edge);
+
+ // This is how we'll know we've gone all the way around
+ int endVertex = coordInds[c % 3 == 0 ? c + 2 : c - 1];
+
+ // Start at current triangle
+ int cur = c;
+
+ // Proceed from one triangle to the next
+ do {
+ // Look up edge in Edge Table to find neighbor triangle
+ Integer tableVal = et.get(edge.v2, edge.v1);
+ if ((DEBUG & 32) != 0) {
+ System.out.println(
+ " Search Edge: " + (new Edge(edge.v2, edge.v1)));
+ }
+
+ // See if there is no triangle on the other side of this edge
+ if (tableVal == null) {
+ smooth = false;
+ if ((DEBUG & 32) != 0)
+ System.out.println(" No neighboring triangle found.");
+ } else {
+
+ int n = tableVal.intValue();
+ if ((DEBUG & 32) != 0) {
+ System.out.println(
+ " Table lookup result: " + n + " (vertex " + coordInds[n] +
+ ")");
+ System.out.print(" Triangles " + (cur/3) + " & " + (n/3) +
+ ": ");
+ }
+
+ cosine = facetNorms[cur / 3].dot(facetNorms[n / 3]);
+ smooth = cosine > threshold;
+ if (smooth) {
+ // The center coordinate (c) shares the same normal in these
+ // two triangles. Find that coordinate and set its index
+ // normalMap[n] = normalMap[cur];
+ int centerv = (((n + 1) % 3) == 0 ? n - 2 : n + 1);
+ if (coordInds[c] != coordInds[centerv]) {
+ centerv = ((n % 3) == 0 ? n + 2 : n - 1);
+ }
+
+ if ((DEBUG & 32) != 0)
+ System.out.println("Smooth! Adding " + centerv);
+
+ if (normalMap[centerv] != Integer.MAX_VALUE) {
+ smooth = false;
+ if ((DEBUG & 32) != 0) System.out.println(
+ " Error: Coordinate aleady has normal (bad data).");
+ } else {
+
+ normalMap[centerv] = tally.size() - 1;
+
+ // Consider this triangle's facet normal when calculating the
+ // vertex's normal
+ sharers.add(new Integer(centerv));
+ if (sharers.size() > maxShare) maxShare = sharers.size();
+
+ // Continue on around the vertex to the next triangle
+ cur = n;
+ if (goingRight) edge.v2 = coordInds[cur];
+ else edge.v1 = coordInds[cur];
+ }
+ } else if ((DEBUG & 32) != 0) System.out.println("Hard Edge!");
+ }
+
+ if (!smooth && goingRight) {
+
+ // We've hit an impasse going right, so now try going left
+ // from the original triangle
+ goingRight = false;
+ smooth = true; // Trick do loop
+ cur = c; // Go back to original triangle
+
+ edge = new Edge(coordInds[(c % 3) == 0 ? c + 2 : c - 1],
+ coordInds[c]);
+ if ((DEBUG & 32) != 0) System.out.println( " Left edge: " + edge);
+
+ }
+
+ } while (smooth && ((goingRight && (edge.v2 != endVertex)) ||
+ !goingRight));
+
+ if (((DEBUG & 32) != 0) && goingRight && (edge.v2 == endVertex))
+ System.out.println(" Went all the way around!");
+ }
+ }
+
+ if ((DEBUG & 32) != 0) {
+ System.out.println("Tally:");
+ for (int i = 0 ; i < tally.size() ; i++) {
+ System.out.print(" " + i + ": ");
+ ArrayList sharers = (ArrayList)(tally.get(i));
+ for (int j = 0 ; j < sharers.size() ; j++) {
+ System.out.print(" " + sharers.get(j));
+ }
+ System.out.println();
+ }
+
+ System.out.println("Normal Indexes:");
+ for (int i = 0 ; i < normalMap.length ; i++) {
+ System.out.println(" " + i + ": " + normalMap[i]);
+ }
+ }
+
+ return maxShare;
+ } // End of createHardEdges
+
+
+ // Now take all of the triangles who share a vertex (who have
+ // been grouped by the hard edge process) and average their facet
+ // normals to get the vertex normal
+ //
+ // This routine has something of a hack in it. We found that our
+ // method of breaking up data into individual triangles before
+ // calculating normals was causing a bug. If a polygon was broken
+ // into two triangles at a particular vertex, then that facet's
+ // normal would get averaged into the vertex normal *twice*,
+ // skewing the normal toward the decomposed facet. So what we did
+ // was to check for duplicate facet normals as we're averaging,
+ // not allowing the same facet normal to be counted twice.
+ //
+ // What should be done is to put the facets' normals into a separate,
+ // indexed, table. That way, to tell if two triangles have the
+ // same normal, we just need to compare indexes. This would speed up
+ // the process of checking for duplicates.
+ private void calculateVertexNormals(int maxShare)
+ {
+ Vector3f normals[];
+ ArrayList sharers;
+ int triangle;
+ Vector3f fn[]; // Normals of facets joined by this vertex
+ int fnsize; // Number of elements currently ised in fn
+
+ if (creaseAngle != 0.0) {
+ fn = new Vector3f[maxShare];
+ normals = new Vector3f[tally.size()];
+ normalInds = new int[coordInds.length];
+ for (int n = 0 ; n < tally.size() ; n++) {
+ sharers = (ArrayList)(tally.get(n));
+ if ((DEBUG & 128) != 0) {
+ System.out.println(n + ": " + sharers.size() +
+ " triangles:");
+ }
+ fnsize = 0;
+ normals[n] = new Vector3f();
+ for (int t = 0 ; t < sharers.size() ; t++) {
+ int v = ((Integer)sharers.get(t)).intValue();
+ // See if index removed by hard edge process
+ if (v != -1) {
+ triangle = v / 3;
+ if (!Float.isNaN(facetNorms[triangle].x)) {
+
+ int f;
+ // Don't add the same facet normal twice
+ for (f = 0 ; f < fnsize ; f++) {
+ if (fn[f].equals(facetNorms[triangle])) break;
+ }
+
+ normalInds[v] = n;
+ if (f == fnsize) {
+ // Didn't find this triangle's normal already in the list
+ normals[n].add(facetNorms[triangle]);
+ fn[fnsize++] = facetNorms[triangle];
+ } else if ((DEBUG & 128) != 0) {
+ System.out.println(" triangle " + t + " ignored.");
+ }
+ }
+ }
+ }
+ normals[n].normalize();
+ if (Float.isNaN(normals[n].x)) {
+ // Normal isn't valid
+ normals[n].x = 1.0f; normals[n].y = normals[n].z = 0.0f;
+ }
+ if ((DEBUG & 128) != 0) {
+ for (int t = 0 ; t < sharers.size() ; t++) {
+ int v = ((Integer)sharers.get(t)).intValue();
+ if (v != -1) {
+ triangle = v / 3;
+ System.out.println(" " + facetNorms[triangle]);
+ }
+ }
+ System.out.println(" Result: " + normals[n]);
+ System.out.println();
+ }
+ }
+ } else {
+ // This code renders the facet normals
+ normals = facetNorms;
+
+ normalInds = new int[facetNorms.length * 3];
+ for (int i = 0 ; i < facetNorms.length ; i++) {
+ normalInds[i * 3 + 0] = i;
+ normalInds[i * 3 + 1] = i;
+ normalInds[i * 3 + 2] = i;
+ }
+ }
+ gi.setNormals(normals);
+
+ if ((DEBUG & 4) != 0) {
+ System.out.println("Normals:");
+ for (int i = 0 ; i < normals.length ; i++) {
+ System.out.println(" " + i + " " + normals[i]);
+ }
+ System.out.println("Indices:");
+ for (int i = 0 ; i < normalInds.length ; i++) {
+ System.out.println(" " + i + " " + normalInds[i]);
+ }
+ }
+ } // End of calculateVertexNormals
+
+
+
+ // The original data was in quads and we converted it to triangles to
+ // calculate the normals. Now we are converting it back to quads.
+ // It's a very simple algorithm.
+ // Since both sub-triangles of a quad have the same facet normal,
+ // there should never be a hard edge down the middle of the quad.
+ // Therefore, the vertices of the shared edge of the two subtriangles
+ // should have the same normal index in both triangles.
+ private int[] triToQuadIndices(int oldList[])
+ {
+ if (oldList == null) return null;
+
+ int newList[] = new int[oldList.length / 6 * 4];
+ // index list to pass back
+ // Cycle through each pair of triangles and put them together
+ for (int q = 0 ; q < oldList.length / 6 ; q++) {
+ newList[q * 4 + 0] = oldList[q * 6 + 0];
+ newList[q * 4 + 1] = oldList[q * 6 + 1];
+ newList[q * 4 + 2] = oldList[q * 6 + 2];
+ newList[q * 4 + 3] = oldList[q * 6 + 5];
+ }
+
+ return newList;
+ } // End of triToQuadIndices
+
+
+
+ // The original data was in quads. We converted it to triangles to
+ // calculate normals. Now we need to convert it back to quads.
+ private void convertTriToQuad(GeometryInfo geom)
+ {
+ // Create the new arrays
+ geom.setCoordinateIndices(
+ triToQuadIndices(geom.getCoordinateIndices()));
+ geom.setColorIndices(triToQuadIndices(geom.getColorIndices()));
+ geom.setNormalIndices(triToQuadIndices(geom.getNormalIndices()));
+ int num = geom.getTexCoordSetCount();
+ for (int i = 0 ; i < num ; i++) {
+ geom.setTextureCoordinateIndices(i,
+ triToQuadIndices(geom.getTextureCoordinateIndices(i)));
+ }
+ geom.setPrimitive(gi.QUAD_ARRAY);
+ } // End of convertTriToQuad()
+
+
+
+ // The original data was in fans and we converted it to triangles to
+ // calculate the normals. Now we are converting it back to fans.
+ // We have already calculated the new stripCounts, so now we need
+ // to change the index lists so they match up with the stripCounts.
+ // It's a very simple algorithm. The paramater oldList is the
+ // index list being compressed back into fans (could be coordinate,
+ // color, normal, or texCoord indices) and numVerts is the pre-
+ // calculated total of all entries of the stripCounts array.
+ private int[] triToFanIndices(int sc[], int oldList[], int numVerts)
+ {
+ if (oldList == null) return null;
+
+ int newList[] = new int[numVerts]; // index list to pass back
+ int vert1 = 0; // 1st vertex of triangle
+ int n = 0; // index into newList
+ // Cycle through each fan in the new list
+ for (int f = 0 ; f < sc.length ; f++) {
+ // Copy entire first triangle into new array
+ newList[n++] = oldList[vert1++];
+ newList[n++] = oldList[vert1++];
+ newList[n++] = oldList[vert1++];
+ // Each additional triangle in the fan only needs one vertex
+ for (int t = 3 ; t < sc[f] ; t++) {
+ newList[n++] = oldList[vert1 + 2];
+ vert1 += 3;
+ }
+ }
+ return newList;
+ } // End of triToFanIndices
+
+
+
+ //
+ // The original data was in fans. We converted it to triangles to
+ // calculate normals. Now we need to convert it back to fans.
+ // The hard part is that, if we found a hard edge in the middle of
+ // a fan, we need to split the fan into two. To tell if there's
+ // a hard edge there, we compare the normal indices of both
+ // vertices comprising the edge.
+ private void convertTriToFan(GeometryInfo geom, int oldStripCounts[])
+ {
+ int ni[] = geom.getNormalIndices();
+
+ //
+ // Calculate new stripCounts array
+ //
+ int tri = 0; // Which triangle currently being converted
+ ArrayList newStripCounts;
+ newStripCounts = new ArrayList(oldStripCounts.length + 100);
+
+ // Use the original stripCounts array
+ for (int f = 0 ; f < oldStripCounts.length ; f++) {
+ int stripCount = 3;
+
+ // Cycle through each triangle in the fan, comparing to the
+ // next triangle in the fan. Compare the normal indices of
+ // both vertices of the edge to see if the two triangles
+ // can be mated
+ for (int t = 0 ; t < oldStripCounts[f] - 3 ; t++) {
+ // The first vertex of this triangle must match the first
+ // vertex of the next, AND the third vertex of this
+ // triangle must match the second vertex of the next.
+ if ((ni[tri * 3] == ni[(tri+1) * 3]) &&
+ (ni[tri * 3 + 2] == ni[(tri+1) * 3 + 1])) {
+ // OK to extend fan
+ stripCount++;
+ } else {
+ // hard edge within fan
+ newStripCounts.add(new Integer(stripCount));
+ stripCount = 3;
+ }
+ tri++;
+ }
+ tri++;
+ newStripCounts.add(new Integer(stripCount));
+ }
+
+ // Convert from ArrayList to int[]
+ int sc[] = new int[newStripCounts.size()];
+ for (int i = 0 ; i < sc.length ; i++)
+ sc[i] = ((Integer)newStripCounts.get(i)).intValue();
+ newStripCounts = null;
+
+ //
+ // Change the index lists so they match up with the new stripCounts
+ //
+
+ // See how many vertices we'll need
+ int c = 0;
+ for (int i = 0 ; i < sc.length ; i++) c += sc[i];
+
+ // Create the new arrays
+ geom.setCoordinateIndices(
+ triToFanIndices(sc, geom.getCoordinateIndices(), c));
+ geom.setColorIndices(triToFanIndices(sc, geom.getColorIndices(), c));
+ geom.setNormalIndices(triToFanIndices(sc, geom.getNormalIndices(), c));
+ int num = geom.getTexCoordSetCount();
+ for (int i = 0 ; i < num ; i++) {
+ geom.setTextureCoordinateIndices(i,
+ triToFanIndices(sc, geom.getTextureCoordinateIndices(i), c));
+ }
+
+ if ((DEBUG & 8) != 0) {
+ System.out.print("Old stripCounts:");
+ for (int i = 0 ; i < oldStripCounts.length ; i++) {
+ System.out.print(" " + oldStripCounts[i]);
+ }
+ System.out.println();
+ System.out.print("New stripCounts:");
+ for (int i = 0 ; i < sc.length ; i++) {
+ System.out.print(" " + sc[i]);
+ }
+ System.out.println();
+ }
+
+ geom.setStripCounts(sc);
+ geom.setPrimitive(gi.TRIANGLE_FAN_ARRAY);
+ } // End of convertTriToFan()
+
+
+
+ // The original data was in strips and we converted it to triangles to
+ // calculate the normals. Now we are converting it back to strips.
+ // We have already calculated the new stripCounts, so now we need
+ // to change the index lists so they match up with the stripCounts.
+ // It's a very simple algorithm. The paramater oldList is the
+ // index list being compressed back into strips (could be coordinate,
+ // color, normal, or texCoord indices) and numVerts is the pre-
+ // calculated total of all entries of the stripCounts array.
+ private int[] triToStripIndices(int sc[], int oldList[], int numVerts)
+ {
+ if (oldList == null) return null;
+
+ int newList[] = new int[numVerts]; // index list to pass back
+ int vert1 = 0; // 1st vertex of triangle
+ int n = 0; // index into newList
+ // Cycle through each strip in the new list
+ for (int f = 0 ; f < sc.length ; f++) {
+ // Copy entire first triangle into new array
+ newList[n++] = oldList[vert1++];
+ newList[n++] = oldList[vert1++];
+ newList[n++] = oldList[vert1++];
+ // Each additional triangle in the fan only needs one vertex
+ for (int t = 3 ; t < sc[f] ; t++) {
+ // Every other triangle has been reversed to preserve winding
+ newList[n++] = oldList[vert1 + 2 - (t % 2)];
+ vert1 += 3;
+ }
+ }
+ return newList;
+ } // End of triToStripIndices
+
+
+
+ private void convertTriToStrip(GeometryInfo geom, int oldStripCounts[])
+ {
+ int ni[] = geom.getNormalIndices();
+
+ //
+ // Calculate new stripCounts array
+ //
+ int tri = 0; // Which triangle currently being converted
+ ArrayList newStripCounts;
+ newStripCounts = new ArrayList(oldStripCounts.length + 100);
+
+ // Use the original stripCounts array
+ for (int f = 0 ; f < oldStripCounts.length ; f++) {
+ int stripCount = 3;
+
+ // Cycle through each triangle in the strip, comparing to the
+ // next triangle in the strip. Compare the normal indices of
+ // both vertices of the edge to see if the two triangles
+ // can be mated.
+ for (int t = 0 ; t < oldStripCounts[f] - 3 ; t++) {
+ // Every other triangle has been reversed to preserve winding
+ if (t % 2 == 0) {
+ // The middle vertex of this triangle needs to match the
+ // first vertex of the next, AND the third vertices of
+ // the two triangles must match
+ if ((ni[tri * 3 + 1] == ni[(tri+1) * 3]) &&
+ (ni[tri * 3 + 2] == ni[(tri+1) * 3 + 2])) {
+ // OK to extend strip
+ stripCount++;
+ } else {
+ // hard edge within strip
+ newStripCounts.add(new Integer(stripCount));
+ stripCount = 3;
+
+ // Can't start a new strip on an odd edge so output
+ // isolated triangle
+ if (t < oldStripCounts[f] - 4) {
+ newStripCounts.add(new Integer(3));
+ t++;
+ }
+ }
+ } else {
+ // The middle vertex of this triangle must match the middle
+ // vertex of the next, AND the third vertex of this triangle
+ // must match the first vertex of the next
+ if ((ni[tri * 3 + 1] == ni[(tri+1) * 3 + 1]) &&
+ (ni[tri * 3 + 2] == ni[(tri+1) * 3])) {
+ // OK to extend strip
+ stripCount++;
+ } else {
+ // hard edge within strip
+ newStripCounts.add(new Integer(stripCount));
+ stripCount = 3;
+ }
+ }
+ tri++;
+ }
+ tri++;
+ newStripCounts.add(new Integer(stripCount));
+ }
+
+ // Convert from ArrayList to int[]
+ int sc[] = new int[newStripCounts.size()];
+ for (int i = 0 ; i < sc.length ; i++)
+ sc[i] = ((Integer)newStripCounts.get(i)).intValue();
+ newStripCounts = null;
+
+ //
+ // Change the index lists so they match up with the new stripCounts
+ //
+
+ // See how many vertices we'll need
+ int c = 0;
+ for (int i = 0 ; i < sc.length ; i++) c += sc[i];
+
+ // Create the new arrays
+ geom.setCoordinateIndices(
+ triToStripIndices(sc, geom.getCoordinateIndices(), c));
+ geom.setColorIndices(triToStripIndices(sc, geom.getColorIndices(), c));
+ geom.setNormalIndices(triToStripIndices(sc, geom.getNormalIndices(), c));
+ int num = geom.getTexCoordSetCount();
+ for (int i = 0 ; i < num ; i++) {
+ geom.setTextureCoordinateIndices(i,
+ triToStripIndices(sc, geom.getTextureCoordinateIndices(i), c));
+ }
+
+ if ((DEBUG & 8) != 0) {
+ System.out.print("Old stripCounts:");
+ for (int i = 0 ; i < oldStripCounts.length ; i++) {
+ System.out.print(" " + oldStripCounts[i]);
+ }
+ System.out.println();
+ System.out.print("New stripCounts:");
+ for (int i = 0 ; i < sc.length ; i++) {
+ System.out.print(" " + sc[i]);
+ }
+ System.out.println();
+ }
+
+ geom.setStripCounts(sc);
+ geom.setPrimitive(gi.TRIANGLE_STRIP_ARRAY);
+ }// End of convertTriToStrip()
+
+
+
+ /**
+ * Used when the user calls the NormalGenerator and not
+ * the Stripifier or Triangulator. We had to convert
+ * the user's data to indexed triangles before we could
+ * generate normals, so now we need to switch back to
+ * the original format.
+ */
+ void convertBackToOldPrim(GeometryInfo geom, int oldPrim,
+ int oldStripCounts[])
+ {
+ if (oldPrim == geom.TRIANGLE_ARRAY) return;
+
+ switch (oldPrim) {
+ case GeometryInfo.QUAD_ARRAY:
+ convertTriToQuad(geom);
+ break;
+ case GeometryInfo.TRIANGLE_FAN_ARRAY:
+ convertTriToFan(geom, oldStripCounts);
+ break;
+ case GeometryInfo.TRIANGLE_STRIP_ARRAY:
+ convertTriToStrip(geom, oldStripCounts);
+ break;
+ }
+
+ if ((DEBUG & 64) != 0) {
+ System.out.println("Coordinate and normal indices (original):");
+ for (int i = 0 ; i < coordInds.length ; i++) {
+ System.out.println(i + " " + coordInds[i] + " " + normalInds[i]);
+ }
+ }
+ } // End of convertBackToOldPrim
+
+
+
+ /**
+ * Generate normals for the GeometryInfo object. If the GeometryInfo
+ * object didn't previously contain indexed data, indexes are made
+ * by collapsing identical positions into a single index. Any
+ * normal information previously contained in the GeometryInfo
+ * object is lost. Strips and Fans are converted into individual
+ * triangles for Normal generation, but are stitched back together
+ * if GeometryInfo.getGeometryArray() (or getIndexedGeometryArray())
+ * is called without stripifying first.
+ */
+ public void generateNormals(GeometryInfo geom)
+ {
+ gi = geom;
+ gi.setNormals((Vector3f[])null);
+ gi.setNormalIndices(null);
+
+ long time = 0L;
+ if ((DEBUG & 16) != 0) {
+ time = System.currentTimeMillis();
+ }
+
+ if (gi.getPrimitive() == gi.POLYGON_ARRAY) {
+ if (tr == null) tr = new Triangulator();
+ tr.triangulate(gi);
+ } else {
+ // NOTE: We should calculate facet normals before converting to
+ // triangles.
+ gi.rememberOldPrim();
+ gi.convertToIndexedTriangles();
+ }
+
+ // Cache some of the GeometryInfo fields
+ coordInds = gi.getCoordinateIndices();
+ colorInds = gi.getColorIndices();
+ normalInds = gi.getNormalIndices();
+ numTexSets = gi.getTexCoordSetCount();
+ texInds = new int[numTexSets][];
+ for (int i = 0 ; i < numTexSets ; i++) {
+ texInds[i] = gi.getTextureCoordinateIndices(i);
+ }
+ stripCounts = gi.getStripCounts();
+
+ if ((DEBUG & 16) != 0) {
+ t1 += System.currentTimeMillis() - time;
+ System.out.println("Convert to triangles: " + t1 + " ms");
+ time = System.currentTimeMillis();
+ }
+
+ calculatefacetNorms();
+ if ((DEBUG & 16) != 0) {
+ t2 += System.currentTimeMillis() - time;
+ System.out.println("Calculate Facet Normals: " + t2 + " ms");
+ time = System.currentTimeMillis();
+ }
+
+ int maxShare = createHardEdges();
+ if ((DEBUG & 16) != 0) {
+ t3 += System.currentTimeMillis() - time;
+ System.out.println("Hard Edges: " + t3 + " ms");
+ time = System.currentTimeMillis();
+ }
+
+ calculateVertexNormals(maxShare);
+ if ((DEBUG & 16) != 0) {
+ t5 += System.currentTimeMillis() - time;
+ System.out.println("Vertex Normals: " + t5 + " ms");
+ time = System.currentTimeMillis();
+ }
+
+ if ((DEBUG & 64) != 0) {
+ System.out.println("Coordinate and normal indices (triangles):");
+ for (int i = 0 ; i < coordInds.length ; i++) {
+ System.out.println(i + " " + coordInds[i] + " " + normalInds[i]);
+ }
+ }
+
+ // We have been caching some info from the GeometryInfo, so we need
+ // to update it.
+ gi.setCoordinateIndices(coordInds);
+ gi.setColorIndices(colorInds);
+ gi.setNormalIndices(normalInds);
+ for (int i = 0 ; i < numTexSets ; i++) {
+ gi.setTextureCoordinateIndices(i, texInds[i]);
+ }
+ gi.setStripCounts(stripCounts);
+ } // End of generateNormals
+
+
+
+ /**
+ * Set the crease angle.
+ * If two triangles' normals differ by more than
+ * creaseAngle, then the vertex will get two separate normals, creating a
+ * discontinuous crease in the model. This is perfect for the edge
+ * of a table or the corner of a cube, for instance. Clamped to
+ * 0 <= creaseAngle <= PI. Optimizations are made for creaseAngle == 0
+ * (facet normals) and creaseAngle == PI (smooth shading).
+ */
+ public void setCreaseAngle(double radians)
+ {
+ if (radians > Math.PI) radians = Math.PI;
+ if (radians < 0.0) radians = 0.0;
+ creaseAngle = radians;
+ } // End of setCreaseAngle
+
+
+
+ /**
+ * Returns the current value of the crease angle, in radians.
+ */
+ public double getCreaseAngle()
+ {
+ return creaseAngle;
+ } // End of getCreaseAngle
+
+
+
+ /**
+ * Constructor. Construct a NormalGenerator object with creaseAngle
+ * set to the given value.
+ */
+ public NormalGenerator(double radians)
+ {
+ creaseAngle = radians;
+ } // End of NormalGenerator(double)
+
+
+
+ /**
+ * Constructor. Construct a NormalGenerator object with creaseAngle
+ * set to 44 degrees (0.767944871 radians).
+ */
+ public NormalGenerator()
+ {
+ this(44.0 * Math.PI / 180.0);
+ } // End of NormalGenerator()
+} // End of class NormalGenerator
+
+// End of file NormalGenerator.java
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Numerics.java b/src/classes/share/com/sun/j3d/utils/geometry/Numerics.java
new file mode 100644
index 0000000..386123b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Numerics.java
@@ -0,0 +1,644 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+import java.io.*;
+import java.util.*;
+import javax.vecmath.*;
+
+class Numerics {
+
+ static double max3(double a, double b, double c)
+ {
+ return (((a) > (b)) ? (((a) > (c)) ? (a) : (c))
+ : (((b) > (c)) ? (b) : (c)));
+ }
+
+ static double min3(double a, double b, double c)
+ {
+ return (((a) < (b)) ? (((a) < (c)) ? (a) : (c))
+ : (((b) < (c)) ? (b) : (c)));
+ }
+
+ static boolean lt(double a, double eps)
+ {
+ return ((a) < -eps);
+ }
+
+ static boolean le(double a, double eps)
+ {
+ return (a <= eps);
+ }
+
+ static boolean ge(double a, double eps)
+ {
+ return (!((a) <= -eps));
+ }
+
+ static boolean eq(double a, double eps)
+ {
+ return (((a) <= eps) && !((a) < -eps));
+ }
+
+ static boolean gt(double a, double eps)
+ {
+ return !((a) <= eps);
+ }
+
+ static double baseLength(Tuple2f u, Tuple2f v) {
+ double x, y;
+ x = (v).x - (u).x;
+ y = (v).y - (u).y;
+ return Math.abs(x) + Math.abs(y);
+ }
+
+ static double sideLength(Tuple2f u, Tuple2f v) {
+ double x, y;
+ x = (v).x - (u).x;
+ y = (v).y - (u).y;
+ return x * x + y * y;
+ }
+
+ /**
+ * This checks whether i3, which is collinear with i1, i2, is
+ * between i1, i2. note that we rely on the lexicographic sorting of the
+ * points!
+ */
+ static boolean inBetween(int i1, int i2, int i3) {
+ return ((i1 <= i3) && (i3 <= i2));
+ }
+
+ static boolean strictlyInBetween(int i1, int i2, int i3) {
+ return ((i1 < i3) && (i3 < i2));
+ }
+
+ /**
+ * this method computes the determinant det(points[i],points[j],points[k])
+ * in a consistent way.
+ */
+ static double stableDet2D(Triangulator triRef, int i, int j, int k) {
+ double det;
+ Point2f numericsHP, numericsHQ, numericsHR;
+
+ // if((triRef.inPointsList(i)==false)||(triRef.inPointsList(j)==false)||
+ // (triRef.inPointsList(k)==false))
+ // System.out.println("Numerics.stableDet2D Not inPointsList " + i + " " + j
+ // + " " + k);
+
+ if ((i == j) || (i == k) || (j == k)) {
+ det = 0.0;
+ }
+ else {
+ numericsHP = triRef.points[i];
+ numericsHQ = triRef.points[j];
+ numericsHR = triRef.points[k];
+
+ if (i < j) {
+ if (j < k) /* i < j < k */
+ det = Basic.det2D(numericsHP, numericsHQ, numericsHR);
+ else if (i < k) /* i < k < j */
+ det = -Basic.det2D(numericsHP, numericsHR, numericsHQ);
+ else /* k < i < j */
+ det = Basic.det2D(numericsHR, numericsHP, numericsHQ);
+ }
+ else {
+ if (i < k) /* j < i < k */
+ det = -Basic.det2D(numericsHQ, numericsHP, numericsHR);
+ else if (j < k) /* j < k < i */
+ det = Basic.det2D(numericsHQ, numericsHR, numericsHP);
+ else /* k < j < i */
+ det = -Basic.det2D(numericsHR, numericsHQ, numericsHP);
+ }
+ }
+
+ return det;
+ }
+
+ /**
+ * Returns the orientation of the triangle.
+ * @return +1 if the points i, j, k are given in CCW order;
+ * -1 if the points i, j, k are given in CW order;
+ * 0 if the points i, j, k are collinear.
+ */
+ static int orientation(Triangulator triRef, int i, int j, int k) {
+ int ori;
+ double numericsHDet;
+ numericsHDet = stableDet2D(triRef, i, j, k);
+ // System.out.println("orientation : numericsHDet " + numericsHDet);
+ if (lt(numericsHDet, triRef.epsilon)) ori = -1;
+ else if (gt(numericsHDet, triRef.epsilon)) ori = 1;
+ else ori = 0;
+ return ori;
+ }
+
+ /**
+ * This method checks whether l is in the cone defined by i, j and j, k
+ */
+ static boolean isInCone(Triangulator triRef, int i, int j, int k,
+ int l, boolean convex) {
+ boolean flag;
+ int numericsHOri1, numericsHOri2;
+
+ // if((triRef.inPointsList(i)==false)||(triRef.inPointsList(j)==false)||
+ // (triRef.inPointsList(k)==false)||(triRef.inPointsList(l)==false))
+ // System.out.println("Numerics.isInCone Not inPointsList " + i + " " + j
+ // + " " + k + " " + l);
+
+ flag = true;
+ if (convex) {
+ if (i != j) {
+ numericsHOri1 = orientation(triRef, i, j, l);
+ // System.out.println("isInCone : i != j, numericsHOri1 = " + numericsHOri1);
+ if (numericsHOri1 < 0) flag = false;
+ else if (numericsHOri1 == 0) {
+ if (i < j) {
+ if (!inBetween(i, j, l)) flag = false;
+ }
+ else {
+ if (!inBetween(j, i, l)) flag = false;
+ }
+ }
+ }
+ if ((j != k) && (flag == true)) {
+ numericsHOri2 = orientation(triRef, j, k, l);
+ // System.out.println("isInCone : ((j != k) && (flag == true)), numericsHOri2 = " +
+ // numericsHOri2);
+ if (numericsHOri2 < 0) flag = false;
+ else if (numericsHOri2 == 0) {
+ if (j < k) {
+ if (!inBetween(j, k, l)) flag = false;
+ }
+ else {
+ if (!inBetween(k, j, l)) flag = false;
+ }
+ }
+ }
+ }
+ else {
+ numericsHOri1= orientation(triRef, i, j, l);
+ if (numericsHOri1 <= 0) {
+ numericsHOri2 = orientation(triRef, j, k, l);
+ if (numericsHOri2 < 0) flag = false;
+ }
+ }
+ return flag;
+ }
+
+
+ /**
+ * Returns convex angle flag.
+ * @return 0 ... if angle is 180 degrees <br>
+ * 1 ... if angle between 0 and 180 degrees <br>
+ * 2 ... if angle is 0 degrees <br>
+ * -1 ... if angle between 180 and 360 degrees <br>
+ * -2 ... if angle is 360 degrees <br>
+ */
+ static int isConvexAngle(Triangulator triRef, int i, int j, int k, int ind) {
+ int angle;
+ double numericsHDot;
+ int numericsHOri1;
+ Point2f numericsHP, numericsHQ;
+
+ // if((triRef.inPointsList(i)==false)||(triRef.inPointsList(j)==false)||
+ // (triRef.inPointsList(k)==false))
+ // System.out.println("Numerics.isConvexAngle: Not inPointsList " + i + " " + j
+ // + " " + k);
+
+ if (i == j) {
+ if (j == k) {
+ // all three vertices are identical; we set the angle to 1 in
+ // order to enable clipping of j.
+ return 1;
+ }
+ else {
+ // two of the three vertices are identical; we set the angle to 1
+ // in order to enable clipping of j.
+ return 1;
+ }
+ }
+ else if (j == k) {
+ // two vertices are identical. we could either determine the angle
+ // by means of yet another lengthy analysis, or simply set the
+ // angle to -1. using -1 means to err on the safe side, as all the
+ // incarnations of this vertex will be clipped right at the start
+ // of the ear-clipping algorithm. thus, eventually there will be no
+ // other duplicates at this vertex position, and the regular
+ // classification of angles will yield the correct answer for j.
+ return -1;
+ }
+ else {
+ numericsHOri1 = orientation(triRef, i, j, k);
+ // System.out.println("i " + i + " j " + j + " k " + k + " ind " + ind +
+ // ". In IsConvexAngle numericsHOri1 is " +
+ // numericsHOri1);
+ if (numericsHOri1 > 0) {
+ angle = 1;
+ }
+ else if (numericsHOri1 < 0) {
+ angle = -1;
+ }
+ else {
+ // 0, 180, or 360 degrees.
+ numericsHP = new Point2f();
+ numericsHQ = new Point2f();
+ Basic.vectorSub2D(triRef.points[i], triRef.points[j], numericsHP);
+ Basic.vectorSub2D(triRef.points[k], triRef.points[j], numericsHQ);
+ numericsHDot = Basic.dotProduct2D(numericsHP, numericsHQ);
+ if (numericsHDot < 0.0) {
+ // 180 degrees.
+ angle = 0;
+ }
+ else {
+ // 0 or 360 degrees? this cannot be judged locally, and more
+ // work is needed.
+
+ angle = spikeAngle(triRef, i, j, k, ind);
+ // System.out.println("SpikeAngle return is "+ angle);
+ }
+ }
+ }
+ return angle;
+ }
+
+
+ /**
+ * This method checks whether point i4 is inside of or on the boundary
+ * of the triangle i1, i2, i3.
+ */
+ static boolean pntInTriangle(Triangulator triRef, int i1, int i2, int i3, int i4) {
+ boolean inside;
+ int numericsHOri1;
+
+ inside = false;
+ numericsHOri1 = orientation(triRef, i2, i3, i4);
+ if (numericsHOri1 >= 0) {
+ numericsHOri1 = orientation(triRef, i1, i2, i4);
+ if (numericsHOri1 >= 0) {
+ numericsHOri1 = orientation(triRef, i3, i1, i4);
+ if (numericsHOri1 >= 0) inside = true;
+ }
+ }
+ return inside;
+ }
+
+
+ /**
+ * This method checks whether point i4 is inside of or on the boundary
+ * of the triangle i1, i2, i3. it also returns a classification if i4 is
+ * on the boundary of the triangle (except for the edge i2, i3).
+ */
+ static boolean vtxInTriangle(Triangulator triRef, int i1, int i2, int i3,
+ int i4, int[] type) {
+ boolean inside;
+ int numericsHOri1;
+
+ inside = false;
+ numericsHOri1 = orientation(triRef, i2, i3, i4);
+ if (numericsHOri1 >= 0) {
+ numericsHOri1 = orientation(triRef, i1, i2, i4);
+ if (numericsHOri1 > 0) {
+ numericsHOri1 = orientation(triRef, i3, i1, i4);
+ if (numericsHOri1 > 0) {
+ inside = true;
+ type[0] = 0;
+ }
+ else if (numericsHOri1 == 0) {
+ inside = true;
+ type[0] = 1;
+ }
+ }
+ else if (numericsHOri1 == 0) {
+ numericsHOri1 = orientation(triRef, i3, i1, i4);
+ if (numericsHOri1 > 0) {
+ inside = true;
+ type[0] = 2;
+ }
+ else if (numericsHOri1 == 0) {
+ inside = true;
+ type[0] = 3;
+ }
+ }
+ }
+ return inside;
+ }
+
+
+ /**
+ * Checks whether the line segments i1, i2 and i3, i4 intersect. no
+ * intersection is reported if they intersect at a common vertex.
+ * the function assumes that i1 <= i2 and i3 <= i4. if i3 or i4 lies
+ * on i1, i2 then an intersection is reported, but no intersection is
+ * reported if i1 or i2 lies on i3, i4. this function is not symmetric!
+ */
+ static boolean segIntersect(Triangulator triRef, int i1, int i2, int i3,
+ int i4, int i5) {
+ int ori1, ori2, ori3, ori4;
+
+ // if((triRef.inPointsList(i1)==false)||(triRef.inPointsList(i2)==false)||
+ // (triRef.inPointsList(i3)==false)||(triRef.inPointsList(i4)==false))
+ // System.out.println("Numerics.segIntersect Not inPointsList " + i1 + " " + i2
+ // + " " + i3 + " " + i4);
+ //
+ // if((i1 > i2) || (i3 > i4))
+ // System.out.println("Numerics.segIntersect i1>i2 or i3>i4 " + i1 + " " + i2
+ // + " " + i3 + " " + i4);
+
+ if ((i1 == i2) || (i3 == i4)) return false;
+ if ((i1 == i3) && (i2 == i4)) return true;
+
+ if ((i3 == i5) || (i4 == i5)) ++(triRef.identCntr);
+
+ ori3 = orientation(triRef, i1, i2, i3);
+ ori4 = orientation(triRef, i1, i2, i4);
+ if (((ori3 == 1) && (ori4 == 1)) ||
+ ((ori3 == -1) && (ori4 == -1))) return false;
+
+ if (ori3 == 0) {
+ if (strictlyInBetween(i1, i2, i3)) return true;
+ if (ori4 == 0) {
+ if (strictlyInBetween(i1, i2, i4)) return true;
+ }
+ else return false;
+ }
+ else if (ori4 == 0) {
+ if (strictlyInBetween(i1, i2, i4)) return true;
+ else return false;
+ }
+
+ ori1 = orientation(triRef, i3, i4, i1);
+ ori2 = orientation(triRef, i3, i4, i2);
+ if (((ori1 <= 0) && (ori2 <= 0)) ||
+ ((ori1 >= 0) && (ori2 >= 0))) return false;
+
+ return true;
+ }
+
+
+ /**
+ * this function computes a quality measure of a triangle i, j, k.
+ * it returns the ratio `base / height', where base is the length of the
+ * longest side of the triangle, and height is the normal distance
+ * between the vertex opposite of the base side and the base side. (as
+ * usual, we again use the l1-norm for distances.)
+ */
+ static double getRatio(Triangulator triRef, int i, int j, int k) {
+ double area, a, b, c, base, ratio;
+ Point2f p, q, r;
+
+ // if((triRef.inPointsList(i)==false)||(triRef.inPointsList(j)==false)||
+ // (triRef.inPointsList(k)==false))
+ // System.out.println("Numerics.getRatio: Not inPointsList " + i + " " + j
+ // + " " + k);
+
+ p = triRef.points[i];
+ q = triRef.points[j];
+ r = triRef.points[k];
+
+
+ a = baseLength(p, q);
+ b = baseLength(p, r);
+ c = baseLength(r, q);
+ base = max3(a, b, c);
+
+ if ((10.0 * a) < Math.min(b, c)) return 0.1;
+
+ area = stableDet2D(triRef, i, j, k);
+ if (lt(area, triRef.epsilon)) {
+ area = -area;
+ }
+ else if (!gt(area, triRef.epsilon)) {
+ if (base > a) return 0.1;
+ else return Double.MAX_VALUE;
+ }
+
+ ratio = base * base / area;
+
+ if (ratio < 10.0) return ratio;
+ else {
+ if (a < base) return 0.1;
+ else return ratio;
+ }
+ }
+
+
+ static int spikeAngle(Triangulator triRef, int i, int j, int k, int ind) {
+ int ind1, ind2, ind3;
+ int i1, i2, i3;
+
+ // if((triRef.inPointsList(i)==false)||(triRef.inPointsList(j)==false)||
+ // (triRef.inPointsList(k)==false))
+ // System.out.println("Numerics.spikeAngle: Not inPointsList " + i + " " + j
+ // + " " + k);
+
+ ind2 = ind;
+ i2 = triRef.fetchData(ind2);
+
+ // if(i2 != j)
+ // System.out.println("Numerics.spikeAngle: i2 != j " + i2 + " " + j );
+
+ ind1 = triRef.fetchPrevData(ind2);
+ i1 = triRef.fetchData(ind1);
+
+ // if(i1 != i)
+ // System.out.println("Numerics.spikeAngle: i1 != i " + i1 + " " + i );
+
+ ind3 = triRef.fetchNextData(ind2);
+ i3 = triRef.fetchData(ind3);
+
+ // if(i3 != k)
+ // System.out.println("Numerics.spikeAngle: i3 != k " + i3 + " " + k );
+
+ return recSpikeAngle(triRef, i, j, k, ind1, ind3);
+ }
+
+
+
+ static int recSpikeAngle(Triangulator triRef, int i1, int i2, int i3,
+ int ind1, int ind3) {
+ int ori, ori1, ori2, i0, ii1, ii2;
+ Point2f pq, pr;
+ double dot;
+
+ if (ind1 == ind3) {
+ // all points are collinear??? well, then it does not really matter
+ // which angle is returned. perhaps, -2 is the best bet as my code
+ // likely regards this contour as a hole.
+ return -2;
+ }
+
+ if (i1 != i3) {
+ if (i1 < i2) {
+ ii1 = i1;
+ ii2 = i2;
+ }
+ else {
+ ii1 = i2;
+ ii2 = i1;
+ }
+ if (inBetween(ii1, ii2, i3)) {
+ i2 = i3;
+ ind3 = triRef.fetchNextData(ind3);
+ i3 = triRef.fetchData(ind3);
+
+ if (ind1 == ind3) return 2;
+ ori = orientation(triRef, i1, i2, i3);
+ if (ori > 0) return 2;
+ else if (ori < 0) return -2;
+ else return recSpikeAngle(triRef, i1, i2, i3, ind1, ind3);
+ }
+ else {
+ i2 = i1;
+ ind1 = triRef.fetchPrevData(ind1);
+ i1 = triRef.fetchData(ind1);
+ if (ind1 == ind3) return 2;
+ ori = orientation(triRef, i1, i2, i3);
+ if (ori > 0) return 2;
+ else if (ori < 0) return -2;
+ else return recSpikeAngle(triRef, i1, i2, i3, ind1, ind3);
+ }
+ }
+ else {
+ i0 = i2;
+ i2 = i1;
+ ind1 = triRef.fetchPrevData(ind1);
+ i1 = triRef.fetchData(ind1);
+
+ if (ind1 == ind3) return 2;
+ ind3 = triRef.fetchNextData(ind3);
+ i3 = triRef.fetchData(ind3);
+ if (ind1 == ind3) return 2;
+ ori = orientation(triRef, i1, i2, i3);
+ if (ori > 0) {
+ ori1 = orientation(triRef, i1, i2, i0);
+ if (ori1 > 0) {
+ ori2 = orientation(triRef, i2, i3, i0);
+ if (ori2 > 0) return -2;
+ }
+ return 2;
+ }
+ else if (ori < 0) {
+ ori1 = orientation(triRef, i2, i1, i0);
+ if (ori1 > 0) {
+ ori2 = orientation(triRef, i3, i2, i0);
+ if (ori2 > 0) return 2;
+ }
+ return -2;
+ }
+ else {
+ pq = new Point2f();
+ Basic.vectorSub2D(triRef.points[i1], triRef.points[i2], pq);
+ pr = new Point2f();
+ Basic.vectorSub2D(triRef.points[i3], triRef.points[i2], pr);
+ dot = Basic.dotProduct2D(pq, pr);
+ if (dot < 0.0) {
+ ori = orientation(triRef, i2, i1, i0);
+ if (ori > 0) return 2;
+ else return -2;
+ }
+ else {
+ return recSpikeAngle(triRef, i1, i2, i3, ind1, ind3);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * computes the signed angle between p, p1 and p, p2.
+ *
+ * warning: this function does not handle a 180-degree angle correctly!
+ * (this is no issue in our application, as we will always compute
+ * the angle centered at the mid-point of a valid diagonal.)
+ */
+ static double angle(Triangulator triRef, Point2f p, Point2f p1, Point2f p2) {
+ int sign;
+ double angle1, angle2, angle;
+ Point2f v1, v2;
+
+ sign = Basic.signEps(Basic.det2D(p2, p, p1), triRef.epsilon);
+
+ if (sign == 0) return 0.0;
+
+ v1 = new Point2f();
+ v2 = new Point2f();
+ Basic.vectorSub2D(p1, p, v1);
+ Basic.vectorSub2D(p2, p, v2);
+
+ angle1 = Math.atan2(v1.y, v1.x);
+ angle2 = Math.atan2(v2.y, v2.x);
+
+ if (angle1 < 0.0) angle1 += 2.0*Math.PI;
+ if (angle2 < 0.0) angle2 += 2.0*Math.PI;
+
+ angle = angle1 - angle2;
+ if (angle > Math.PI) angle = 2.0*Math.PI - angle;
+ else if (angle < -Math.PI) angle = 2.0*Math.PI + angle;
+
+ if (sign == 1) {
+ if (angle < 0.0) return -angle;
+ else return angle;
+ }
+ else {
+ if (angle > 0.0) return -angle;
+ else return angle;
+ }
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Orientation.java b/src/classes/share/com/sun/j3d/utils/geometry/Orientation.java
new file mode 100644
index 0000000..e790bdd
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Orientation.java
@@ -0,0 +1,173 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+import java.io.*;
+import java.util.*;
+import javax.vecmath.*;
+
+class Orientation {
+
+ /**
+ * determine the outer polygon and the orientation of the polygons; the
+ * default orientation is CCW for the outer-most polygon, and CW for the
+ * inner polygons. the polygonal loops are referenced by loops[i1,..,i2-1].
+ */
+ static void adjustOrientation(Triangulator triRef, int i1, int i2) {
+
+ double area;
+ int i, outer;
+ int ind;
+
+ if(i1 >= i2)
+ System.out.println("Orientation:adjustOrientation Problem i1>=i2 !!!");
+
+ if (triRef.numLoops >= triRef.maxNumPolyArea) {
+ // System.out.println("Orientation:adjustOrientation Expanding polyArea array .");
+ triRef.maxNumPolyArea = triRef.numLoops;
+ double old[] = triRef.polyArea;
+ triRef.polyArea = new double[triRef.maxNumPolyArea];
+ if(old != null)
+ System.arraycopy(old, 0, triRef.polyArea, 0, old.length);
+ }
+
+ // for each contour, compute its signed area, i.e., its orientation. the
+ // contour with largest area is assumed to be the outer-most contour.
+ for (i = i1; i < i2; ++i) {
+ ind = triRef.loops[i];
+ triRef.polyArea[i] = polygonArea(triRef, ind);
+ }
+
+ // determine the outer-most contour
+ area = Math.abs(triRef.polyArea[i1]);
+ outer = i1;
+ for (i = i1 + 1; i < i2; ++i) {
+ if (area < Math.abs(triRef.polyArea[i])) {
+ area = Math.abs(triRef.polyArea[i]);
+ outer = i;
+ }
+ }
+
+ // default: the outer contour is referenced by loops[i1]
+ if (outer != i1) {
+ ind = triRef.loops[i1];
+ triRef.loops[i1] = triRef.loops[outer];
+ triRef.loops[outer] = ind;
+
+ area = triRef.polyArea[i1];
+ triRef.polyArea[i1] = triRef.polyArea[outer];
+ triRef.polyArea[outer] = area;
+ }
+
+ // adjust the orientation
+ if (triRef.polyArea[i1] < 0.0) triRef.swapLinks(triRef.loops[i1]);
+ for (i = i1 + 1; i < i2; ++i) {
+ if (triRef.polyArea[i] > 0.0) triRef.swapLinks(triRef.loops[i]);
+ }
+ }
+
+ /**
+ * This function computes twice the signed area of a simple closed polygon.
+ */
+ static double polygonArea(Triangulator triRef, int ind) {
+ int hook = 0;
+ int ind1, ind2;
+ int i1, i2;
+ double area = 0.0, area1 = 0;
+
+ ind1 = ind;
+ i1 = triRef.fetchData(ind1);
+ ind2 = triRef.fetchNextData(ind1);
+ i2 = triRef.fetchData(ind2);
+ area = Numerics.stableDet2D(triRef, hook, i1, i2);
+
+ ind1 = ind2;
+ i1 = i2;
+ while (ind1 != ind) {
+ ind2 = triRef.fetchNextData(ind1);
+ i2 = triRef.fetchData(ind2);
+ area1 = Numerics.stableDet2D(triRef, hook, i1, i2);
+ area += area1;
+ ind1 = ind2;
+ i1 = i2;
+ }
+
+ return area;
+ }
+
+
+ /**
+ * Determine the orientation of the polygon. The default orientation is CCW.
+ */
+ static void determineOrientation(Triangulator triRef, int ind) {
+ double area;
+
+ // compute the polygon's signed area, i.e., its orientation.
+ area = polygonArea(triRef, ind);
+
+ // adjust the orientation (i.e., make it CCW)
+ if (area < 0.0) {
+ triRef.swapLinks(ind);
+ triRef.ccwLoop = false;
+ }
+
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/PntNode.java b/src/classes/share/com/sun/j3d/utils/geometry/PntNode.java
new file mode 100644
index 0000000..1f246ad
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/PntNode.java
@@ -0,0 +1,70 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+// Placeholder list
+class PntNode extends Object {
+ int pnt;
+ int next;
+
+ PntNode() {
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Primitive.java b/src/classes/share/com/sun/j3d/utils/geometry/Primitive.java
new file mode 100644
index 0000000..2c34b88
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Primitive.java
@@ -0,0 +1,262 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry;
+
+import com.sun.j3d.utils.geometry.*;
+import java.io.*;
+import java.util.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import java.math.*;
+
+/**
+ * Base class for all Java 3D primitives. By default all primitives
+ * with the same parameters share their geometry (e.g., you can have 50
+ * shperes in your scene, but the geometry is stored only once). A
+ * change to one primitive will effect all shared nodes. Another
+ * implication of this implementation is that the capabilities of the
+ * geometry are shared, and once one of the shared nodes is live, the
+ * capabilities cannot be set. Use the GEOMETRY_NOT_SHARED flag if
+ * you do not wish to share geometry among primitives with the same
+ * parameters.
+ */
+
+public abstract class Primitive extends Group {
+ /**
+ * Specifies that normals are generated along with the positions.
+ */
+ public static final int GENERATE_NORMALS = 0x01;
+
+ /**
+ * Specifies that texture coordinates are generated along with the
+ * positions.
+ */
+ public static final int GENERATE_TEXTURE_COORDS = 0x02;
+
+ /**
+ * Specifies that normals are to be flipped along the surface.
+ */
+ public static final int GENERATE_NORMALS_INWARD = 0x04;
+
+ /**
+ * Specifies that the geometry being created will not be shared by
+ * another scene graph node. By default all primitives created with
+ * the same parameters share their geometry (e.g., you can have 50
+ * spheres in your scene, but the geometry is stored only once). A
+ * change to one primitive will effect all shared nodes. You
+ * specify this flag if you do not wish to share any geometry among
+ * primitives of the same parameters. */
+ public static final int GEOMETRY_NOT_SHARED = 0x10;
+
+ /**
+ * Specifies that the ALLOW_INTERSECT
+ * capability bit should be set on the generated geometry.
+ * This allows the object
+ * to be picked using Geometry based picking.
+ */
+ public static final int ENABLE_GEOMETRY_PICKING = 0x20;
+
+ /**
+ * Specifies that the ALLOW_APPEARANCE_READ and
+ * ALLOW_APPEARANCE_WRITE bits are to be set on the generated
+ * geometry's Shape3D nodes.
+ */
+ public static final int ENABLE_APPEARANCE_MODIFY = 0x40;
+
+ static final int SPHERE = 0x01;
+ static final int CYLINDER = 0x02;
+ static final int CONE = 0x04;
+ static final int BOX = 0x08;
+
+ // used for cached geometries of Cone and Cylinder
+ static final int TOP_DISK = 0x10;
+ static final int BOTTOM_DISK = 0x20;
+ static final int CONE_DIVISIONS = 0x40;
+
+ int numTris = 0;
+ int numVerts = 0;
+
+ /**
+ * Primitive flags.
+ */
+ int flags;
+
+
+ /**
+ * Constructs a default primitive.
+ */
+ public Primitive()
+ {
+ flags = 0;
+ setCapability(ENABLE_PICK_REPORTING);
+ setCapability(ALLOW_CHILDREN_READ);
+ }
+
+ /**
+ * Returns the total number of triangles in this primitive.
+ * @return the total number of triangles in this primitive
+ */
+ public int getNumTriangles() {
+ return numTris;
+ }
+
+ /**
+ * @deprecated The number of triangles is an immutable attribute.
+ */
+ public void setNumTriangles(int num) {
+ System.err.println("Warning: setNumTriangles has no effect");
+ }
+
+ /**
+ * Returns the total number of vertices in this primitive.
+ * @return the total number of vertices in this primitive
+ */
+ public int getNumVertices() {
+ return numVerts;
+ }
+
+ /**
+ * @deprecated The number of vertices is an immutable attribute.
+ */
+ public void setNumVertices(int num) {
+ System.err.println("Warning: setNumVertices has no effect");
+ }
+
+ /** Returns the flags of primitive (generate normal, textures, caching, etc).
+ */
+ public int getPrimitiveFlags()
+ {
+ return flags;
+ }
+
+ /**
+ * @deprecated The primitive flags must be set at construction time
+ * via one of the subclass constructors.
+ */
+ public void setPrimitiveFlags(int fl) {
+ System.err.println("Warning: setPrimitiveFlags has no effect");
+ }
+
+ /** Obtains a shape node of a subpart of the primitive.
+ * @param partid identifier for a given subpart of the primitive.
+ */
+ public abstract Shape3D getShape(int partid);
+
+ /** Gets the appearance of the primitive (defaults to first subpart).
+ */
+ public Appearance getAppearance(){
+ return getShape(0).getAppearance();
+ }
+
+ /**
+ * Gets the appearance of the specified part of the primitive.
+ *
+ * @param partId identifier for a given subpart of the primitive
+ *
+ * @return The appearance object associated with the partID. If an
+ * invalid partId is passed in, null is returned.
+ *
+ * @since Java 3D 1.2.1
+ */
+ public abstract Appearance getAppearance(int partId);
+
+ /** Sets the appearance of a subpart given a partid.
+ */
+
+ public void setAppearance(int partid, Appearance ap)
+ {
+ getShape(partid).setAppearance(ap);
+ }
+
+ /** Sets the main appearance of the primitive (all subparts) to
+ * same appearance.
+ */
+ public abstract void setAppearance(Appearance ap);
+
+
+ /** Sets the main appearance of the primitive (all subparts) to
+ * a default white appearance.
+ */
+ public void setAppearance(){
+
+ Color3f aColor = new Color3f(0.1f, 0.1f, 0.1f);
+ Color3f eColor = new Color3f(0.0f, 0.0f, 0.0f);
+ Color3f dColor = new Color3f(0.6f, 0.6f, 0.6f);
+ Color3f sColor = new Color3f(1.0f, 1.0f, 1.0f);
+
+ Material m = new Material(aColor, eColor, dColor, sColor, 100.0f);
+ Appearance a = new Appearance();
+ m.setLightingEnable(true);
+ a.setMaterial(m);
+ setAppearance(a);
+ }
+
+ static Hashtable geomCache = new Hashtable();
+
+ String strfloat(float x)
+ {
+ return (new Float(x)).toString();
+ }
+
+ protected void cacheGeometry(int kind, float a, float b,
+ float c, int d, int e, int flags,
+ GeomBuffer geo)
+ {
+ String key = new String(kind+strfloat(a)+strfloat(b)+
+ strfloat(c)+d+e+flags);
+ geomCache.put(key, geo);
+ }
+
+ protected GeomBuffer getCachedGeometry(int kind, float a, float b, float c,
+ int d, int e, int flags)
+ {
+ String key = new String(kind+strfloat(a)+strfloat(b)+
+ strfloat(c)+d+e+flags);
+ Object cache = geomCache.get(key);
+
+ return((GeomBuffer) cache);
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Project.java b/src/classes/share/com/sun/j3d/utils/geometry/Project.java
new file mode 100644
index 0000000..e5489e4
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Project.java
@@ -0,0 +1,254 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+import javax.vecmath.*;
+import java.io.*;
+import java.util.*;
+
+class Project {
+
+ /**
+ * This function projects the vertices of the polygons referenced by
+ * loops[i1,..,i2-1] to an approximating plane.
+ */
+ static void projectFace(Triangulator triRef, int loopMin, int loopMax) {
+ Vector3f normal, nr;
+ int i, j;
+ double d;
+
+ normal = new Vector3f();
+ nr = new Vector3f();
+
+ // determine the normal of the plane onto which the points get projected
+ determineNormal(triRef, triRef.loops[loopMin], normal);
+ j = loopMin + 1;
+ if (j < loopMax) {
+ for (i = j; i < loopMax; ++i) {
+ determineNormal(triRef, triRef.loops[i], nr);
+ if (Basic.dotProduct(normal, nr) < 0.0) {
+ Basic.invertVector(nr);
+ }
+ Basic.vectorAdd(normal, nr, normal);
+ }
+ d = Basic.lengthL2(normal);
+ if (Numerics.gt(d, Triangulator.ZERO)) {
+ Basic.divScalar(d, normal);
+ }
+ else {
+ // System.out.println("*** ProjectFace: zero-length normal vector!? ***\n");
+ normal.x = normal.y = 0.0f;
+ normal.z = 1.0f;
+ }
+ }
+
+ // project the points onto this plane. the projected points are stored in
+ // the array `points[0,..,numPoints]'
+
+ // System.out.println("loopMin " + loopMin + " loopMax " + loopMax);
+ projectPoints(triRef, loopMin, loopMax, normal);
+
+ }
+
+
+ /**
+ * This function computes the average of all normals defined by triples of
+ * successive vertices of the polygon. we'll see whether this is a good
+ * heuristic for finding a suitable plane normal...
+ */
+ static void determineNormal(Triangulator triRef, int ind, Vector3f normal) {
+ Vector3f nr, pq, pr;
+ int ind0, ind1, ind2;
+ int i0, i1, i2;
+ double d;
+
+ ind1 = ind;
+ i1 = triRef.fetchData(ind1);
+ ind0 = triRef.fetchPrevData(ind1);
+ i0 = triRef.fetchData(ind0);
+ ind2 = triRef.fetchNextData(ind1);
+ i2 = triRef.fetchData(ind2);
+ pq = new Vector3f();
+ Basic.vectorSub((Tuple3f) triRef.vertices[i0], (Tuple3f) triRef.vertices[i1], (Vector3f) pq);
+ pr = new Vector3f();
+ Basic.vectorSub((Tuple3f) triRef.vertices[i2], (Tuple3f) triRef.vertices[i1], (Vector3f) pr);
+ nr = new Vector3f();
+ Basic.vectorProduct(pq, pr, nr);
+ d = Basic.lengthL2(nr);
+ if (Numerics.gt(d, Triangulator.ZERO)) {
+ Basic.divScalar(d, nr);
+ normal.set(nr);
+ }
+ else {
+ normal.x = normal.y = normal.z = 0.0f;
+ }
+
+ pq.set(pr);
+ ind1 = ind2;
+ ind2 = triRef.fetchNextData(ind1);
+ i2 = triRef.fetchData(ind2);
+ while (ind1 != ind) {
+ Basic.vectorSub((Tuple3f) triRef.vertices[i2], (Tuple3f) triRef.vertices[i1], pr);
+ Basic.vectorProduct(pq, pr, nr);
+ d = Basic.lengthL2(nr);
+ if (Numerics.gt(d, Triangulator.ZERO)) {
+ Basic.divScalar(d, nr);
+ if (Basic.dotProduct(normal, nr) < 0.0) {
+ Basic.invertVector(nr);
+ }
+ Basic.vectorAdd(normal, nr, normal);
+ }
+ pq.set(pr);
+ ind1 = ind2;
+ ind2 = triRef.fetchNextData(ind1);
+ i2 = triRef.fetchData(ind2);
+ }
+
+ d = Basic.lengthL2(normal);
+ if (Numerics.gt(d, Triangulator.ZERO)) {
+ Basic.divScalar(d, normal);
+ }
+ else {
+ //System.out.println("*** DetermineNormal: zero-length normal vector!? ***\n");
+ normal.x = normal.y = 0.0f; normal.z = 1.0f;
+
+ }
+ }
+
+
+ /**
+ * This function maps the vertices of the polygon referenced by `ind' to the
+ * plane n3.x * x + n3.y * y + n3.z * z = 0. every mapped vertex (x,y,z)
+ * is then expressed in terms of (x',y',z'), where z'=0. this is
+ * achieved by transforming the original vertices into a coordinate system
+ * whose z-axis coincides with n3, and whose two other coordinate axes n1
+ * and n2 are orthonormal on n3. note that n3 is supposed to be of unit
+ * length!
+ */
+ static void projectPoints(Triangulator triRef, int i1, int i2, Vector3f n3) {
+ Matrix4f matrix = new Matrix4f();
+ Point3f vtx = new Point3f();
+ Vector3f n1, n2;
+ double d;
+ int ind, ind1;
+ int i, j1;
+
+
+ n1 = new Vector3f();
+ n2 = new Vector3f();
+
+ // choose n1 and n2 appropriately
+ if ((Math.abs(n3.x) > 0.1) || (Math.abs(n3.y) > 0.1)) {
+ n1.x = -n3.y;
+ n1.y = n3.x;
+ n1.z = 0.0f;
+ }
+ else {
+ n1.x = n3.z;
+ n1.z = -n3.x;
+ n1.y = 0.0f;
+ }
+ d = Basic.lengthL2(n1);
+ Basic.divScalar(d, n1);
+ Basic.vectorProduct(n1, n3, n2);
+ d = Basic.lengthL2(n2);
+ Basic.divScalar(d, n2);
+
+ // initialize the transformation matrix
+ matrix.m00 = n1.x;
+ matrix.m01 = n1.y;
+ matrix.m02 = n1.z;
+ matrix.m03 = 0.0f; // translation of the coordinate system
+ matrix.m10 = n2.x;
+ matrix.m11 = n2.y;
+ matrix.m12 = n2.z;
+ matrix.m13 = 0.0f; // translation of the coordinate system
+ matrix.m20 = n3.x;
+ matrix.m21 = n3.y;
+ matrix.m22 = n3.z;
+ matrix.m23 = 0.0f; // translation of the coordinate system
+ matrix.m30 = 0.0f;
+ matrix.m31 = 0.0f;
+ matrix.m32 = 0.0f;
+ matrix.m33 = 1.0f;
+
+ // transform the vertices and store the transformed vertices in the array
+ // `points'
+ triRef.initPnts(20);
+ for (i = i1; i < i2; ++i) {
+ ind = triRef.loops[i];
+ ind1 = ind;
+ j1 = triRef.fetchData(ind1);
+ matrix.transform((Point3f)triRef.vertices[j1], vtx);
+ j1 = triRef.storePoint(vtx.x, vtx.y);
+ triRef.updateIndex(ind1, j1);
+ ind1 = triRef.fetchNextData(ind1);
+ j1 = triRef.fetchData(ind1);
+ while (ind1 != ind) {
+ matrix.transform(triRef.vertices[j1], vtx);
+ j1 = triRef.storePoint(vtx.x, vtx.y);
+ triRef.updateIndex(ind1, j1);
+ ind1 = triRef.fetchNextData(ind1);
+ j1 = triRef.fetchData(ind1);
+ }
+ }
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Quadrics.java b/src/classes/share/com/sun/j3d/utils/geometry/Quadrics.java
new file mode 100644
index 0000000..4af75d9
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Quadrics.java
@@ -0,0 +1,722 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry;
+
+import com.sun.j3d.utils.geometry.*;
+import java.io.*;
+import java.util.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import java.math.*;
+
+class Quadrics extends Object {
+
+ Quadrics (){ }
+
+ // do not use this to make a cone. It uses too many triangles. The top of
+ // cone can be a triangle fan array
+
+ GeomBuffer cylinder(double lowr, double highr,
+ double height, int xdivisions, int ydivisions,
+ boolean outside)
+ {
+ double r, sign;
+
+ /*
+ * Cylinder is created by extruding the unit circle along the Z+
+ * axis. The number of layers is controlled by ydivisions (which is
+ * actually zdivisions). For each consecutive sample points along the
+ * unit circle, we also sample along the height of the cylinder. Quads
+ * are created along the Z direction. When the unit circle at the base is
+ * completed, we also obtain the layers of quads along the height of the
+ * cylinder.
+ *
+ * Texture coordinates are created in a straight forward cylindrical
+ * mapping of the texture map. The texture is wrapped from the back of
+ * the cylinder.
+ */
+
+ if (outside)
+ sign = 1.0;
+ else
+ sign = -1.0;
+
+ //Compute the deltas
+ double dtheta = 2.0*Math.PI / xdivisions;
+ double dr = (highr - lowr) / ydivisions;
+ double dz = height / ydivisions;
+ double znormal = (lowr - highr) / height;
+ double du = 1.0 / xdivisions;
+ double dv = 1.0 / ydivisions;
+
+ //Initialize geometry buffer.
+ GeomBuffer gbuf = new GeomBuffer(xdivisions*ydivisions*4);
+
+ double s = 0.0, t = 0.0;
+ for (int i=0;i<xdivisions;i++) {
+ double px, py, qx, qy, z;
+ // (px,py) and (qx,qy) are consecutive sample points along the
+ // unit circle. We will create quads along the Z+ direction.
+ // we have to start at the back of the sphere, so add 90 degrees
+ px = Math.cos(i*dtheta + Math.PI/2.0);
+ py = Math.sin(i*dtheta + Math.PI/2.0);
+ qx = Math.cos((i+1)*dtheta + Math.PI/2.0);
+ qy = Math.sin((i+1)*dtheta + Math.PI/2.0);
+ // Initialize z,r,t
+ z = -1.0*height/2.0; r = lowr; t = 0.0;
+
+ gbuf.begin( GeomBuffer.QUAD_STRIP );
+ // For each consecutive two unit circle points,
+ // we obtain the layers of quads along the Z direction. Number of
+ // layers depends on ydivisions.
+ for (int j=0; j<=ydivisions; j++,z += dz,r += dr,t += dv){
+ if ((j == ydivisions) && (highr == 0)) {
+ if (outside) {
+ gbuf.normal3d( 0.0, 0.0, znormal*sign );
+ gbuf.texCoord2d(s, t);
+ gbuf.vertex3d( px*r, py*r, z );
+ gbuf.normal3d( 0.0, 0.0, znormal*sign );
+ gbuf.texCoord2d(s+du, t);
+ gbuf.vertex3d( qx*r, qy*r, z );
+ }
+ else {
+ gbuf.normal3d( 0.0, 0.0, znormal*sign );
+ gbuf.texCoord2d(s, t);
+ gbuf.vertex3d( qx*r, qy*r, z );
+ gbuf.normal3d( 0.0, 0.0, znormal*sign );
+ gbuf.texCoord2d(s+du, t);
+ gbuf.vertex3d( px*r, py*r, z );
+ }
+ } else {
+ if (outside) {
+ gbuf.normal3d( px*sign, py*sign, znormal*sign );
+ gbuf.texCoord2d(s, t);
+ gbuf.vertex3d( px*r, py*r, z );
+ gbuf.normal3d( qx*sign, qy*sign, znormal*sign );
+ gbuf.texCoord2d(s+du, t);
+ gbuf.vertex3d( qx*r, qy*r, z );
+ }
+ else {
+ gbuf.normal3d( qx*sign, qy*sign, znormal*sign );
+ gbuf.texCoord2d(s, t);
+ gbuf.vertex3d( qx*r, qy*r, z );
+ gbuf.normal3d( px*sign, py*sign, znormal*sign );
+ gbuf.texCoord2d(s+du, t);
+ gbuf.vertex3d( px*r, py*r, z );
+ }
+ }
+ }
+ gbuf.end();
+ s += du;
+ }
+ return gbuf;
+ }
+
+ GeomBuffer disk(double r, int xdivisions, boolean outside)
+ {
+ double theta, dtheta, sign, sinTheta, cosTheta;
+ int i;
+
+ /*
+ * Disk is created by evaluating points along the unit circle. Let
+ * theta be the angle about the Z axis. Then, for each theta, we
+ * obtain (cos(theta), sin(theta)) = (x,y) sample points. We create quad
+ * strips (or fan strips) from these sample points.
+ *
+ * Texture coordinates of the disk is gotten from a unit circle
+ * centered at (0.5, 0.5) in s,t space. Thus, portions of a texture is not
+ * used.
+ */
+
+ if (outside)
+ sign = 1.0;
+ else sign = -1.0;
+
+ dtheta = 2.0*Math.PI / xdivisions;
+ GeomBuffer gbuf = new GeomBuffer(xdivisions*4);
+
+ // gbuf.begin(GeomBuffer.QUAD_STRIP);
+ gbuf.begin(GeomBuffer.TRIANGLE_FAN);
+ // the first point to add is the center of the fan
+ gbuf.normal3d( 0.0, 0.0, 1.0*sign );
+ gbuf.texCoord2d(0.5,0.5);
+ gbuf.vertex3d(0.0, 0.0, 0.0);
+ if (outside) {
+ for (i = 0;i <= xdivisions;i++) {
+ theta = i * dtheta;
+ // add 90 degrees to theta so lines up with the body
+ sinTheta = Math.sin(theta + Math.PI/2.0);
+ cosTheta = Math.cos(theta + Math.PI/2.0);
+ // First point of quad
+ gbuf.normal3d( 0.0, 0.0, 1.0*sign );
+ // Texture coord is centered at (0.5, 0.5) in s,t space.
+ gbuf.texCoord2d(0.5+cosTheta*0.5,0.5+sinTheta*0.5);
+ gbuf.vertex3d( r*cosTheta, r*sinTheta , 0.0);
+
+ }
+ }
+ else {
+ for (i=xdivisions;i>=0;i--) {
+ theta = i * dtheta;
+ // add 90 degrees to theta so lines up with the body
+ sinTheta = Math.sin(theta + Math.PI/2.0);
+ cosTheta = Math.cos(theta + Math.PI/2.0);
+ gbuf.normal3d( 0.0, 0.0, 1.0*sign );
+ // if not outside, texture coordinates need to be upside down
+ // to conform with VRML spec
+// gbuf.texCoord2d(0.5-cosTheta*0.5, 0.5+sinTheta*0.5);
+ gbuf.texCoord2d(0.5+cosTheta*0.5, 0.5-sinTheta*0.5);
+ gbuf.vertex3d( r*cosTheta, r*sinTheta, 0.0 );
+ }
+ }
+
+ gbuf.end();
+ return gbuf;
+ }
+
+ // use this to make the top of the cone. It uses a triangle fan so there
+ // aren't extra triangles
+
+ GeomBuffer coneTop(double coneRadius, double coneHeight, int xdivisions,
+ int ydivisions, boolean outside) {
+
+ double sign;
+ double radius = coneRadius/(double)ydivisions;
+
+ double bottom = coneHeight/2.0 - coneHeight/(double)ydivisions;
+ double top = coneHeight/2.0;
+
+ if (outside) sign = 1.0;
+ else sign = -1.0;
+
+ // compute the deltas
+ double dtheta = 2.0 * Math.PI / (double)xdivisions;
+ double znormal = radius/(top-bottom);
+ double du = 1.0/(double)xdivisions;
+
+ // initialize geometry buffer
+ GeomBuffer gbuf = new GeomBuffer(xdivisions + 2);
+ gbuf.begin(GeomBuffer.TRIANGLE_FAN);
+ // add the tip, which is the center of the fan
+ gbuf.normal3d(0.0, 0.0, znormal*sign);
+ gbuf.texCoord2d(.5, 1);
+ gbuf.vertex3d(0.0, 0.0, top);
+
+ // go around the circle and add the rest of the fan
+ double s = 0.0;
+ double t = 1.0 - 1.0/(double)ydivisions;
+ double px, py;
+ if (outside) {
+ for (int i = 0; i <= xdivisions; i++) {
+ // we have to start at the back of the sphere, so add 90 degrees
+ px = Math.cos(i*dtheta + Math.PI/2.0);
+ py = Math.sin(i*dtheta + Math.PI/2.0);
+ gbuf.normal3d(px*sign, py*sign, znormal*sign);
+ gbuf.texCoord2d(s, t);
+ gbuf.vertex3d(px*radius, py*radius, bottom);
+ s += du;
+ }
+ }
+ else {
+ for (int i = xdivisions; i >= 0; i--) {
+ px = Math.cos(i*dtheta + Math.PI/2.0);
+ py = Math.sin(i*dtheta + Math.PI/2.0);
+ gbuf.normal3d(px*sign, py*sign, znormal*sign);
+ gbuf.texCoord2d(s, t);
+ gbuf.vertex3d(px*radius, py*radius, bottom);
+ s += du;
+ }
+ }
+ gbuf.end();
+ return gbuf;
+ }
+
+ // use this to make the body of the cone. similar to cylinder, only allows
+ // you to leave off the top.
+
+ GeomBuffer coneBody(double coneRadius, double height,
+ int xdivisions, int ydivisions,
+ boolean outside)
+ {
+ double r, sign;
+
+ double bottom = -height/2.0;
+ double topRadius = coneRadius/(double)ydivisions;
+
+ /*
+ * The cone body is created by extruding the unit circle along the Z+
+ * axis. The number of layers is controlled by ydivisions (which is
+ * actually zdivisions). For each consecutive sample points along the
+ * unit circle, we also sample along the height of the cone. Quads
+ * are created along the Z direction. When the unit circle at the base is
+ * completed, we also obtain the layers of quads along the height of the
+ * cylinder.
+ *
+ * Texture coordinates are created in a straight forward cylindrical
+ * mapping of the texture map. The texture is wrapped from the back of
+ * the cylinder.
+ */
+
+ if (outside)
+ sign = 1.0;
+ else
+ sign = -1.0;
+
+ //Compute the deltas
+ double dtheta = 2.0*Math.PI / xdivisions;
+ double dr = -coneRadius / ydivisions;
+ double dz = height / ydivisions;
+ double znormal = coneRadius / height;
+ double du = 1.0 / xdivisions;
+ double dv = 1.0 / ydivisions;
+
+ //Initialize geometry buffer.
+ GeomBuffer gbuf = new GeomBuffer(xdivisions*ydivisions*4);
+
+ double s = 0.0, t = 0.0;
+ for (int i=0;i<xdivisions;i++) {
+ double px, py, qx, qy, z;
+ // (px,py) and (qx,qy) are consecutive sample points along the
+ // unit circle. We will create quads along the Z+ direction.
+ // we have to start at the back of the sphere, so add 90 degrees
+ px = Math.cos(i*dtheta + Math.PI/2.0);
+ py = Math.sin(i*dtheta + Math.PI/2.0);
+ qx = Math.cos((i+1)*dtheta + Math.PI/2.0);
+ qy = Math.sin((i+1)*dtheta + Math.PI/2.0);
+ // Initialize z,r,t
+ //z = -1.0*height/2.0;
+ r = coneRadius; t = 0.0;
+ z = bottom;
+
+ gbuf.begin( GeomBuffer.QUAD_STRIP );
+ // For each consecutive two unit circle points,
+ // we obtain the layers of quads along the Z direction. Number of
+ // layers depends on ydivisions.
+ for (int j=0; j<=ydivisions-1; j++,z += dz,r += dr,t += dv){
+ if (outside) {
+ gbuf.normal3d( px*sign, py*sign, znormal*sign );
+ gbuf.texCoord2d(s, t);
+ gbuf.vertex3d( px*r, py*r, z );
+ gbuf.normal3d( qx*sign, qy*sign, znormal*sign );
+ gbuf.texCoord2d(s+du, t);
+ gbuf.vertex3d( qx*r, qy*r, z );
+ }
+ else {
+ gbuf.normal3d( qx*sign, qy*sign, znormal*sign );
+ gbuf.texCoord2d(s, t);
+ gbuf.vertex3d( qx*r, qy*r, z );
+ gbuf.normal3d( px*sign, py*sign, znormal*sign );
+ gbuf.texCoord2d(s+du, t);
+ gbuf.vertex3d( px*r, py*r, z );
+ }
+ }
+ gbuf.end();
+ s += du;
+ }
+ return gbuf;
+ }
+
+ // new disk code to remove transforms in the primitive code
+ GeomBuffer disk(double r, int xdiv, double y, boolean outside) {
+
+ double theta, dtheta, sign, sinTheta, cosTheta;
+
+ if (outside) sign = 1.0;
+ else sign = -1.0;
+
+ dtheta = 2.0*Math.PI / xdiv;
+
+ GeomBuffer gbuf = new GeomBuffer(xdiv+2);
+
+ gbuf.begin(GeomBuffer.TRIANGLE_FAN);
+ gbuf.normal3d(0.0, 1.0*sign, 0.0);
+ gbuf.texCoord2d(0.5, 0.5);
+ gbuf.vertex3d(0.0, y, 0.0);
+
+ // create the disk by evaluating points along the unit circle.
+ // theta is the angle around the y-axis. Then we obtain
+ // (cos(theta), sin(theta)) = (x,z) sample points. The y value
+ // was passed in as a parameter.
+ // texture coordinates are obtain from the unit circle centered at
+ // (.5, .5) in s, t space. thus portions of the texture are not used.
+
+ if (!outside) {
+ for (int i = 0; i <= xdiv; i++) {
+ theta = i * dtheta;
+ // add 90 degrees to theta so lines up wtih the body
+ sinTheta = Math.sin(theta - Math.PI/2.0);
+ cosTheta = Math.cos(theta - Math.PI/2.0);
+ gbuf.normal3d(0.0, 1.0*sign, 0.0);
+ gbuf.texCoord2d(0.5+cosTheta*0.5, 0.5+sinTheta*0.5);
+ gbuf.vertex3d(r*cosTheta, y, r*sinTheta);
+ }
+ }
+ else {
+ for (int i = xdiv; i >= 0; i--) {
+ theta = i * dtheta;
+ // add 90 degrees to theta so lines up with the body
+ sinTheta = Math.sin(theta - Math.PI/2.0);
+ cosTheta = Math.cos(theta - Math.PI/2.0);
+ gbuf.normal3d(0.0, 1.0*sign, 0.0);
+ gbuf.texCoord2d(0.5+cosTheta*0.5, 0.5-sinTheta*0.5);
+ gbuf.vertex3d(cosTheta*r, y, sinTheta*r);
+ }
+ }
+
+ gbuf.end();
+ return gbuf;
+ }
+
+
+ // new cylinder to remove transforms in the cylinder code and to optimize
+ // by using triangle strip
+ GeomBuffer cylinder(double height, double radius,
+ int xdiv, int ydiv, boolean outside) {
+
+ double sign;
+
+ if (outside) sign = 1.0;
+ else sign = -1.0;
+
+ // compute the deltas
+ double dtheta = 2.0*Math.PI / (double)xdiv;
+ double dy = height / (double)ydiv;
+ double du = 1.0/(double)xdiv;
+ double dv = 1.0/(double)ydiv;
+
+ GeomBuffer gbuf = new GeomBuffer(ydiv*2*(xdiv+1));
+
+ double s = 0.0, t = 0.0;
+ double px, pz, qx, qz;
+ double py = -height/2.0;
+ double qy;
+
+// int c;
+// if (outside) c = ydiv*2*(xdiv+1) - 1;
+// else c = 0;
+
+ gbuf.begin(GeomBuffer.QUAD_STRIP);
+
+ for (int i = 0; i < ydiv; i++) {
+ qy = py+dy;
+ if (outside) {
+ px = Math.cos(xdiv*dtheta - Math.PI/2.0);
+ pz = Math.sin(xdiv*dtheta - Math.PI/2.0);
+ qx = Math.cos((xdiv-1)*dtheta - Math.PI/2.0);
+ qz = Math.sin((xdiv-1)*dtheta - Math.PI/2.0);
+
+ // vert 2
+ gbuf.normal3d(px*sign, 0.0, pz*sign);
+ gbuf.texCoord2d(s, t+dv);
+ gbuf.vertex3d(px*radius, qy, pz*radius);
+
+ // vert 1
+ gbuf.normal3d(px*sign, 0.0, pz*sign);
+ gbuf.texCoord2d(s, t);
+ gbuf.vertex3d(px*radius, py, pz*radius);
+
+ // vert 4
+ gbuf.normal3d(qx*sign, 0.0, qz*sign);
+ gbuf.texCoord2d(s+du, t+dv);
+ gbuf.vertex3d(qx*radius, qy, qz*radius);
+
+ // vert 3
+ gbuf.normal3d(qx*sign, 0.0, qz*sign);
+ gbuf.texCoord2d(s+du, t);
+ gbuf.vertex3d(qx*radius, py, qz*radius);
+
+ s += (du*2.0);
+
+ for (int j = xdiv-2; j >=0; j--) {
+ px = Math.cos(j*dtheta - Math.PI/2.0);
+ pz = Math.sin(j*dtheta - Math.PI/2.0);
+
+ // vert 6
+ gbuf.normal3d(px*sign, 0.0, pz*sign);
+ gbuf.texCoord2d(s, t+dv);
+ gbuf.vertex3d(px*radius, qy, pz*radius);
+
+ // vert 5
+ gbuf.normal3d(px*sign, 0.0, pz*sign);
+ gbuf.texCoord2d(s, t);
+ gbuf.vertex3d(px*radius, py, pz*radius);
+
+ s += du;
+ }
+
+ }
+ else {
+// c = 0;
+ px = Math.cos(-Math.PI/2.0);
+ pz = Math.sin(-Math.PI/2.0);
+ qx = Math.cos(dtheta - Math.PI/2.0);
+ qz = Math.sin(dtheta - Math.PI/2.0);
+
+ gbuf.normal3d(px*sign, 0.0, pz*sign);
+ gbuf.texCoord2d(s, t+dv);
+ gbuf.vertex3d(px*radius, qy, pz*radius);
+
+ // vert 1
+ gbuf.normal3d(px*sign, 0.0, pz*sign);
+ gbuf.texCoord2d(s, t);
+ gbuf.vertex3d(px*radius, py, pz*radius);
+
+ gbuf.normal3d(qx*sign, 0.0, qz*sign);
+ gbuf.texCoord2d(s+du, t+dv);
+ gbuf.vertex3d(qx*radius, qy, qz*radius);
+
+ gbuf.normal3d(qx*sign, 0.0, qz*sign);
+ gbuf.texCoord2d(s+du, t);
+ gbuf.vertex3d(qx*radius, py, qz*radius);
+
+ s += (du*2.0);
+
+ for (int j = 2; j <= xdiv; j++) {
+ px = Math.cos(j*dtheta - Math.PI/2.0);
+ pz = Math.sin(j*dtheta - Math.PI/2.0);
+
+ gbuf.normal3d(px*sign, 0.0, pz*sign);
+ gbuf.texCoord2d(s, t+dv);
+ gbuf.vertex3d(px*radius, qy, pz*radius);
+
+ gbuf.normal3d(px*sign, 0.0, pz*sign);
+ gbuf.texCoord2d(s, t);
+ gbuf.vertex3d(px*radius, py, pz*radius);
+
+ s += du;
+ }
+
+ }
+ s = 0.0;
+ t += dv;
+ py += dy;
+ }
+
+ gbuf.end();
+
+ return gbuf;
+ }
+
+ // new coneBody method to remove transform in the Cone primitive
+ // and to optimize by using triangle strip
+ GeomBuffer coneBody(double bottom, double top, double bottomR, double topR,
+ int xdiv, int ydiv, double dv, boolean outside) {
+
+ double r, sign;
+
+ if (outside) sign = 1.0;
+ else sign = -1.0;
+
+ // compute the deltas
+ double dtheta = 2.0*Math.PI/(double)xdiv;
+ double dr = (topR-bottomR)/(double)ydiv;
+ double height = top-bottom;
+ double dy = height/(double)ydiv;
+ double ynormal = (bottomR-topR)/height;
+ double du = 1.0/(double)xdiv;
+// double dv = 1.0/(double)(ydiv+1);
+
+ GeomBuffer gbuf = new GeomBuffer(ydiv*2*(xdiv+1));
+
+ double s = 0.0, t = 0.0;
+ double px, pz, qx, qz;
+ double py = bottom;
+ double qy;
+ r = bottomR;
+
+ gbuf.begin(GeomBuffer.QUAD_STRIP);
+
+ for (int i = 0; i < ydiv; i++) {
+ qy = py+dy;
+ if (outside) {
+ px = Math.cos(xdiv*dtheta - Math.PI/2.0);
+ pz = Math.sin(xdiv*dtheta - Math.PI/2.0);
+ qx = Math.cos((xdiv-1)*dtheta - Math.PI/2.0);
+ qz = Math.sin((xdiv-1)*dtheta - Math.PI/2.0);
+
+ // vert2
+ gbuf.normal3d(px*sign, ynormal*sign, pz*sign);
+ gbuf.texCoord2d(s, t+dv);
+ gbuf.vertex3d(px*(r+dr), qy, pz*(r+dr));
+
+ // vert1
+ gbuf.normal3d(px*sign, ynormal*sign, pz*sign);
+ gbuf.texCoord2d(s, t);
+ gbuf.vertex3d(px*r, py, pz*r);
+
+ // vert4
+ gbuf.normal3d(qx*sign, ynormal*sign, qz*sign);
+ gbuf.texCoord2d(s+du, t+dv);
+ gbuf.vertex3d(qx*(r+dr), qy, qz*(r+dr));
+
+ // vert3
+ gbuf.normal3d(qx*sign, ynormal*sign, qz*sign);
+ gbuf.texCoord2d(s+du, t);
+ gbuf.vertex3d(qx*r, py, qz*r);
+
+ s += (du*2.0);
+
+ for (int j = xdiv-2; j >= 0; j--) {
+ px = Math.cos(j*dtheta - Math.PI/2.0);
+ pz = Math.sin(j*dtheta - Math.PI/2.0);
+
+ // vert 6
+ gbuf.normal3d(px*sign, ynormal*sign, pz*sign);
+ gbuf.texCoord2d(s, t+dv);
+ gbuf.vertex3d(px*(r+dr), qy, pz*(r+dr));
+
+ // vert 5
+ gbuf.normal3d(px*sign, ynormal*sign, pz*sign);
+ gbuf.texCoord2d(s, t);
+ gbuf.vertex3d(px*r, py, pz*r);
+
+ s += du;
+ }
+ }
+ else {
+ px = Math.cos(-Math.PI/2.0);
+ pz = Math.sin(-Math.PI/2.0);
+ qx = Math.cos(dtheta - Math.PI/2.0);
+ qz = Math.sin(dtheta - Math.PI/2.0);
+
+ // vert1
+ gbuf.normal3d(px*sign, ynormal*sign, pz*sign);
+ gbuf.texCoord2d(s, t+dv);
+ gbuf.vertex3d(px*(r+dr), qy, pz*(r+dr));
+
+ gbuf.normal3d(px*sign, ynormal*sign, pz*sign);
+ gbuf.texCoord2d(s, t);
+ gbuf.vertex3d(px*r, py, pz*r);
+
+ gbuf.normal3d(qx*sign, ynormal*sign, qz*sign);
+ gbuf.texCoord2d(s+du, t+dv);
+ gbuf.vertex3d(qx*(r+dr), qy, qz*(r+dr));
+
+ gbuf.normal3d(qx*sign, ynormal*sign, qz*sign);
+ gbuf.texCoord2d(s+du, t);
+ gbuf.vertex3d(qx*r, py, qz*r);
+
+ s += (du*2.0);
+
+ for (int j = 2; j <= xdiv; j++) {
+ px = Math.cos(j*dtheta - Math.PI/2.0);
+ pz = Math.sin(j*dtheta - Math.PI/2.0);
+
+ gbuf.normal3d(px*sign, ynormal*sign, pz*sign);
+ gbuf.texCoord2d(s, t+dv);
+ gbuf.vertex3d(px*(r+dr), qy, pz*(r+dr));
+
+ gbuf.normal3d(px*sign, ynormal*sign, pz*sign);
+ gbuf.texCoord2d(s, t);
+ gbuf.vertex3d(px*r, py, pz*r);
+
+ s += du;
+ }
+ }
+ s = 0.0;
+ t += dv;
+ py += dy;
+ r += dr;
+ }
+ gbuf.end();
+
+ return gbuf;
+ }
+
+ // new coneTop method to remove transforms in the cone code
+ GeomBuffer coneTop(double bottom, double radius, double height,
+ int xdiv,double t, boolean outside) {
+
+ double sign;
+
+ if (outside) sign = 1.0;
+ else sign = -1.0;
+
+ // compute the deltas
+ double dtheta = 2.0*Math.PI/(double)xdiv;
+ double ynormal = radius/height;
+ double du = 1.0/(double)xdiv;
+ double top = bottom + height;
+
+ // initialize the geometry buffer
+ GeomBuffer gbuf = new GeomBuffer(xdiv + 2);
+ gbuf.begin(GeomBuffer.TRIANGLE_FAN);
+
+ // add the tip, which is the center of the fan
+ gbuf.normal3d(0.0, ynormal*sign, 0.0);
+ gbuf.texCoord2d(.5, 1.0);
+ gbuf.vertex3d(0.0, top, 0.0);
+
+ // go around the circle and add the rest of the fan
+ double s = 0.0;
+ double px, pz;
+ if (outside) {
+// for (int i = 0; i <= xdiv; i++) {
+ for (int i = xdiv; i >= 0; i--) {
+ px = Math.cos(i*dtheta - Math.PI/2.0);
+ pz = Math.sin(i*dtheta - Math.PI/2.0);
+ gbuf.normal3d(px*sign, ynormal*sign, pz*sign);
+ gbuf.texCoord2d(s, t);
+ gbuf.vertex3d(px*radius, bottom, pz*radius);
+
+ s += du;
+ }
+ }
+ else {
+// for (int i = xdiv; i >= 0; i--) {
+ for (int i = 0; i <= xdiv; i++) {
+ px = Math.cos(i*dtheta - Math.PI/2.0);
+ pz = Math.sin(i*dtheta - Math.PI/2.0);
+ gbuf.normal3d(px*sign, ynormal*sign, pz*sign);
+ gbuf.texCoord2d(s, t);
+ gbuf.vertex3d(px*radius, bottom, pz*radius);
+ s += du;
+ }
+ }
+ gbuf.end();
+ return gbuf;
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Simple.java b/src/classes/share/com/sun/j3d/utils/geometry/Simple.java
new file mode 100644
index 0000000..c4abd56
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Simple.java
@@ -0,0 +1,225 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+import java.io.*;
+import java.util.*;
+import javax.vecmath.*;
+
+
+class Simple {
+
+ /**
+ * Handle a triangle or a quadrangle in a simple and efficient way. if the
+ * face is more complex than false is returned.
+ *
+ * warning: the correctness of this function depends upon the fact that
+ * `CleanPolyhedralFace' has not yet been executed; i.e., the
+ * vertex indices have not been changed since the execution of
+ * `CleanPolyhedron'! (otherwise, we would have to get the original
+ * indices via calls to `GetOriginal'...)
+ */
+ static boolean simpleFace(Triangulator triRef, int ind1) {
+ int ind0, ind2, ind3, ind4;
+ int i1, i2, i3, i0, i4;
+
+ Point3f pq, pr, nr;
+
+ double x, y, z;
+ int ori2, ori4;
+
+ ind0 = triRef.fetchPrevData(ind1);
+ i0 = triRef.fetchData(ind0);
+
+ if (ind0 == ind1) {
+ // this polygon has only one vertex! nothing to triangulate...
+ System.out.println("***** polygon with only one vertex?! *****\n");
+ return true;
+ }
+
+ ind2 = triRef.fetchNextData(ind1);
+ i2 = triRef.fetchData(ind2);
+ if (ind0 == ind2) {
+ // this polygon has only two vertices! nothing to triangulate...
+ System.out.println("***** polygon with only two vertices?! *****\n");
+ return true;
+ }
+
+ ind3 = triRef.fetchNextData(ind2);
+ i3 = triRef.fetchData(ind3);
+ if (ind0 == ind3) {
+ // this polygon is a triangle! let's triangulate it!
+ i1 = triRef.fetchData(ind1);
+ // triRef.storeTriangle(i1, i2, i3);
+ triRef.storeTriangle(ind1, ind2, ind3);
+ return true;
+ }
+
+ ind4 = triRef.fetchNextData(ind3);
+ i4 = triRef.fetchData(ind4);
+ if (ind0 == ind4) {
+ // this polygon is a quadrangle! not too hard to triangulate it...
+ // we project the corners of the quadrangle onto one of the coordinate
+ // planes
+ triRef.initPnts(5);
+ i1 = triRef.fetchData(ind1);
+
+ pq = new Point3f();
+ pr = new Point3f();
+ nr = new Point3f();
+ /*
+ System.out.println("ind0 " + ind0 + ", ind1 " + ind1 + ", ind2 " +
+ ind2 + ", ind3 " + ind3 + ", ind4 " + ind4);
+ System.out.println("i0 " + i0 +", i1 " + i1 + ", i2 " + i2 +
+ ", i3 " + i3 + ", i4 " + i4);
+
+ System.out.println("vert[i1] " + triRef.vertices[i1] +
+ "vert[i2] " + triRef.vertices[i2] +
+ "vert[i3] " + triRef.vertices[i3]);
+ */
+
+ Basic.vectorSub(triRef.vertices[i1], triRef.vertices[i2], pq);
+ Basic.vectorSub(triRef.vertices[i3], triRef.vertices[i2], pr);
+ Basic.vectorProduct(pq, pr, nr);
+
+ // System.out.println("pq " + pq + " pr " + pr + " nr " + nr);
+ x = Math.abs(nr.x);
+ y = Math.abs(nr.y);
+ z = Math.abs(nr.z);
+ if ((z >= x) && (z >= y)) {
+ // System.out.println("((z >= x) && (z >= y))");
+ triRef.points[1].x = triRef.vertices[i1].x;
+ triRef.points[1].y = triRef.vertices[i1].y;
+ triRef.points[2].x = triRef.vertices[i2].x;
+ triRef.points[2].y = triRef.vertices[i2].y;
+ triRef.points[3].x = triRef.vertices[i3].x;
+ triRef.points[3].y = triRef.vertices[i3].y;
+ triRef.points[4].x = triRef.vertices[i4].x;
+ triRef.points[4].y = triRef.vertices[i4].y;
+ }
+ else if ((x >= y) && (x >= z)) {
+ // System.out.println("((x >= y) && (x >= z))");
+ triRef.points[1].x = triRef.vertices[i1].z;
+ triRef.points[1].y = triRef.vertices[i1].y;
+ triRef.points[2].x = triRef.vertices[i2].z;
+ triRef.points[2].y = triRef.vertices[i2].y;
+ triRef.points[3].x = triRef.vertices[i3].z;
+ triRef.points[3].y = triRef.vertices[i3].y;
+ triRef.points[4].x = triRef.vertices[i4].z;
+ triRef.points[4].y = triRef.vertices[i4].y;
+ }
+ else {
+ triRef.points[1].x = triRef.vertices[i1].x;
+ triRef.points[1].y = triRef.vertices[i1].z;
+ triRef.points[2].x = triRef.vertices[i2].x;
+ triRef.points[2].y = triRef.vertices[i2].z;
+ triRef.points[3].x = triRef.vertices[i3].x;
+ triRef.points[3].y = triRef.vertices[i3].z;
+ triRef.points[4].x = triRef.vertices[i4].x;
+ triRef.points[4].y = triRef.vertices[i4].z;
+ }
+ triRef.numPoints = 5;
+
+ // find a valid diagonal
+ ori2 = Numerics.orientation(triRef, 1, 2, 3);
+ ori4 = Numerics.orientation(triRef, 1, 3, 4);
+
+ /*
+ for(int i=0; i<5; i++)
+ System.out.println("point " + i + ", " + triRef.points[i]);
+ System.out.println("ori2 : " + ori2 + " ori4 : " + ori4);
+ */
+
+ if (((ori2 > 0) && (ori4 > 0)) ||
+ ((ori2 < 0) && (ori4 < 0))) {
+
+ // i1, i3 is a valid diagonal;
+ //
+ // encode as a 2-triangle strip: the triangles are (2, 3, 1)
+ // and (1, 3, 4).
+
+ // triRef.storeTriangle(i1, i2, i3);
+ // triRef.storeTriangle(i1, i3, i4);
+ triRef.storeTriangle(ind1, ind2, ind3);
+ triRef.storeTriangle(ind1, ind3, ind4);
+ }
+ else {
+ // i2, i4 has to be a valid diagonal. (if this is no valid
+ // diagonal then the corners of the quad form a figure of eight;
+ // shall we apply any heuristics in order to guess which diagonal
+ // is more likely to be the better choice? alternatively, we could
+ // return false and subject it to the standard triangulation
+ // algorithm. well, let's see how this brute-force solution works.)
+
+ // encode as a 2-triangle strip: the triangles are (1, 2, 4)
+ // and (4, 2, 3).
+
+ // triRef.storeTriangle(i2, i3, i4);
+ // triRef.storeTriangle(i2, i4, i1);
+ triRef.storeTriangle(ind2, ind3, ind4);
+ triRef.storeTriangle(ind2, ind4, ind1);
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Sphere.java b/src/classes/share/com/sun/j3d/utils/geometry/Sphere.java
new file mode 100644
index 0000000..8d5442e
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Sphere.java
@@ -0,0 +1,502 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry;
+
+import com.sun.j3d.utils.geometry.*;
+import java.io.*;
+import java.util.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import java.math.*;
+
+/**
+ * Sphere is a geometry primitive created with a given radius and resolution.
+ * It is centered at the origin.
+ * <p>
+ * When a texture is applied to a Sphere, it is mapped CCW from the back
+ * of the sphere.
+ * <p>
+ * By default all primitives with the same parameters share their
+ * geometry (e.g., you can have 50 shperes in your scene, but the
+ * geometry is stored only once). A change to one primitive will
+ * effect all shared nodes. Another implication of this
+ * implementation is that the capabilities of the geometry are shared,
+ * and once one of the shared nodes is live, the capabilities cannot
+ * be set. Use the GEOMETRY_NOT_SHARED flag if you do not wish to
+ * share geometry among primitives with the same parameters.
+ */
+
+public class Sphere extends Primitive {
+
+ /**
+ * Sphere shape identifier, used by <code>getShape</code>.
+ *
+ * @see Sphere#getShape
+ */
+ public static final int BODY = 0;
+
+ static final int MID_REZ_DIV = 16;
+ float radius;
+ int divisions;
+
+ /**
+ * Constructs a Sphere of a given radius. Normals are generated
+ * by default, texture coordinates are not. The resolution defaults to
+ * 15 divisions along sphere's axes. Appearance defaults to white.
+ * @param radius Radius
+ */
+ public Sphere (float radius) {
+ this(radius, GENERATE_NORMALS, MID_REZ_DIV);
+ }
+
+ /**
+ * Constructs a default Sphere of radius of 1.0. Normals are generated
+ * by default, texture coordinates are not.
+ * Resolution defaults to 15 divisions. Appearance defaults to white.
+ */
+ public Sphere() {
+ this(1.0f, GENERATE_NORMALS, MID_REZ_DIV);
+ }
+
+ /**
+ * Constructs a Sphere of a given radius and appearance.
+ * Normals are generated by default, texture coordinates are not.
+ * @param radius Radius
+ * @param ap Appearance
+ */
+
+ public Sphere (float radius, Appearance ap) {
+ this(radius, GENERATE_NORMALS, MID_REZ_DIV, ap);
+ }
+
+ /**
+ * Constructs a Sphere of a given radius and appearance with
+ * additional parameters specified by the Primitive flags.
+ * @param radius Radius
+ * @param primflags
+ * @param ap appearance
+ */
+ public Sphere(float radius, int primflags, Appearance ap) {
+ this(radius, primflags, MID_REZ_DIV, ap);
+ }
+
+ /**
+ * Constructs a Sphere of a given radius and number of divisions
+ * with additional parameters specified by the Primitive flags.
+ * Appearance defaults to white.
+ * @param radius Radius
+ * @param divisions Divisions
+ * @param primflags Primflags
+ */
+ public Sphere(float radius, int primflags, int divisions) {
+ this(radius, primflags, divisions, null);
+ }
+
+
+ /**
+ * Obtains Sphere's shape node that contains the geometry.
+ * This allows users to modify the appearance or geometry.
+ * @param partId The part to return (must be BODY for Spheres)
+ * @return The Shape3D object associated with the partId. If an
+ * invalid partId is passed in, null is returned.
+ */
+ public Shape3D getShape(int partId) {
+ if (partId != BODY) return null;
+// return (Shape3D)((Group)getChild(0)).getChild(BODY);
+ return (Shape3D)getChild(BODY);
+ }
+
+ /** Obtains Sphere's shape node that contains the geometry.
+ */
+ public Shape3D getShape() {
+// return (Shape3D)((Group)getChild(0)).getChild(BODY);
+ return (Shape3D)getChild(BODY);
+ }
+
+ /** Sets appearance of the Sphere.
+ */
+ public void setAppearance(Appearance ap) {
+// ((Shape3D)((Group)getChild(0)).getChild(BODY)).setAppearance(ap);
+ ((Shape3D)getChild(BODY)).setAppearance(ap);
+ }
+
+ /**
+ * Gets the appearance of the specified part of the sphere.
+ *
+ * @param partId identifier for a given subpart of the sphere
+ *
+ * @return The appearance object associated with the partID. If an
+ * invalid partId is passed in, null is returned.
+ *
+ * @since Java 3D 1.2.1
+ */
+ public Appearance getAppearance(int partId) {
+ if (partId != BODY) return null;
+ return getShape(partId).getAppearance();
+ }
+
+
+ /**
+ * Constructs a customized Sphere of a given radius,
+ * number of divisions, and appearance, with additional parameters
+ * specified by the Primitive flags. The resolution is defined in
+ * terms of number of subdivisions along the sphere's axes. More
+ * divisions lead to more finely tesselated objects.
+ * <p>
+ * If the appearance is null, the sphere defaults to a white appearance.
+ */
+ public Sphere(float radius, int primflags, int divisions, Appearance ap) {
+ super();
+
+ int sign;
+ int n, nstep;
+
+ this.radius = radius;
+ this.divisions = divisions;
+
+ /*
+ * The sphere algorithm evaluates spherical angles along regular
+ * units. For each spherical coordinate, (theta, rho), a (x,y,z) is
+ * evaluated (along with the normals and texture coordinates).
+ *
+ * The spherical angles theta varies from 0 to 2pi and rho from 0
+ * to pi. Sample points depends on the number of divisions.
+ */
+
+ flags = primflags;
+
+ //Depending on whether normal inward bit is set.
+ if ((flags & GENERATE_NORMALS_INWARD) != 0) {
+ sign = -1;
+ } else {
+ sign = 1;
+ }
+
+ if (divisions < 4) {
+ nstep = 1;
+ n = 4;
+ } else {
+ int mod = divisions % 4;
+ if (mod == 0) {
+ n = divisions;
+ } else {
+ n = divisions + (4 - mod);
+ }
+ nstep = n/4;
+ }
+
+
+ GeomBuffer cache = getCachedGeometry(Primitive.SPHERE,
+ radius, 0.0f, 0.0f,
+ divisions, 0, primflags);
+
+ Shape3D shape;
+
+ if (cache != null) {
+ shape = new Shape3D(cache.getComputedGeometry());
+ numVerts += cache.getNumVerts();
+ numTris += cache.getNumTris();
+ } else {
+ // buffer size = 8*(1 + 2E{i} + (nstep+1))
+ // where E{i} = sum of i = 2 ... nstep
+ GeomBuffer gbuf = new GeomBuffer(8*nstep*(nstep+2));
+
+ for (int i=0; i < 4; i++) {
+ buildQuadrant(gbuf, i*Math.PI/2, (i+1)*Math.PI/2, sign, nstep, n, true);
+ buildQuadrant(gbuf, i*Math.PI/2, (i+1)*Math.PI/2, sign, nstep, n, false);
+ }
+
+ shape = new Shape3D(gbuf.getGeom(flags));
+ numVerts = gbuf.getNumVerts();
+ numTris = gbuf.getNumTris();
+ if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
+ cacheGeometry(Primitive.SPHERE,
+ radius, 0.0f, 0.0f,
+ divisions, 0, primflags, gbuf);
+ }
+ }
+
+ if ((flags & ENABLE_APPEARANCE_MODIFY) != 0) {
+ shape.setCapability(Shape3D.ALLOW_APPEARANCE_READ);
+ shape.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
+ }
+
+ if ((flags & ENABLE_GEOMETRY_PICKING) != 0) {
+ shape.setCapability(Shape3D.ALLOW_GEOMETRY_READ);
+ }
+
+ this.addChild(shape);
+
+ if (ap == null) {
+ setAppearance();
+ }
+ else setAppearance(ap);
+ }
+
+ /**
+ * Used to create a new instance of the node. This routine is called
+ * by <code>cloneTree</code> to duplicate the current node.
+ * <code>cloneNode</code> should be overridden by any user subclassed
+ * objects. All subclasses must have their <code>cloneNode</code>
+ * method consist of the following lines:
+ * <P><blockquote><pre>
+ * public Node cloneNode(boolean forceDuplicate) {
+ * UserSubClass usc = new UserSubClass();
+ * usc.duplicateNode(this, forceDuplicate);
+ * return usc;
+ * }
+ * </pre></blockquote>
+ * @param forceDuplicate when set to <code>true</code>, causes the
+ * <code>duplicateOnCloneTree</code> flag to be ignored. When
+ * <code>false</code>, the value of each node's
+ * <code>duplicateOnCloneTree</code> variable determines whether
+ * NodeComponent data is duplicated or copied.
+ *
+ * @see Node#cloneTree
+ * @see Node#duplicateNode
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public Node cloneNode(boolean forceDuplicate) {
+ Sphere s = new Sphere(radius, flags, divisions, getAppearance());
+ s.duplicateNode(this, forceDuplicate);
+
+ return s;
+ }
+
+ /**
+ * Copies all node information from <code>originalNode</code> into
+ * the current node. This method is called from the
+ * <code>cloneNode</code> method which is, in turn, called by the
+ * <code>cloneTree</code> method.
+ * <P>
+ * For any <i>NodeComponent</i> objects
+ * contained by the object being duplicated, each <i>NodeComponent</i>
+ * object's <code>duplicateOnCloneTree</code> value is used to determine
+ * whether the <i>NodeComponent</i> should be duplicated in the new node
+ * or if just a reference to the current node should be placed in the
+ * new node. This flag can be overridden by setting the
+ * <code>forceDuplicate</code> parameter in the <code>cloneTree</code>
+ * method to <code>true</code>.
+ *
+ * @param originalNode the original node to duplicate.
+ * @param forceDuplicate when set to <code>true</code>, causes the
+ * <code>duplicateOnCloneTree</code> flag to be ignored. When
+ * <code>false</code>, the value of each node's
+ * <code>duplicateOnCloneTree</code> variable determines whether
+ * NodeComponent data is duplicated or copied.
+ *
+ * @see Node#cloneTree
+ * @see Node#cloneNode
+ * @see NodeComponent#setDuplicateOnCloneTree
+ */
+ public void duplicateNode(Node originalNode, boolean forceDuplicate) {
+ super.duplicateNode(originalNode, forceDuplicate);
+ }
+
+ /**
+ * Returns the radius of the sphere
+ *
+ * @since Java 3D 1.2.1
+ */
+ public float getRadius() {
+ return radius;
+ }
+
+ /**
+ * Returns the number of divisions
+ *
+ * @since Java 3D 1.2.1
+ */
+ public int getDivisions() {
+ return divisions;
+ }
+
+ void buildQuadrant(GeomBuffer gbuf, double startDelta, double endDelta,
+ int sign, int nstep, int n, boolean upperSphere)
+ {
+
+ double ds, dt, theta, delta;
+ int i, j, index, i2;
+ double h, r, vx, vz;
+ Point3f pt;
+ Vector3f norm;
+ TexCoord2f texCoord;
+ double starth;
+ double t;
+ boolean leftToRight;
+
+
+ if (upperSphere) {
+ dt = Math.PI/(2*nstep);
+ theta = dt;
+ starth = 1;
+ leftToRight = (sign > 0);
+ } else {
+ dt = -Math.PI/(2*nstep);
+ theta = Math.PI + dt;
+ starth = -1;
+ leftToRight = (sign < 0);
+ }
+
+
+ for (i = 1; i <= nstep; i++) {
+ h = Math.cos(theta);
+ r = Math.sin(theta);
+ if (sign > 0) {
+ t = 1 - theta/Math.PI;
+ } else {
+ t = theta/Math.PI;
+ }
+
+ i2 = i << 1;
+ // subdivision decreases towards the pole
+ ds = (endDelta - startDelta) / i;
+
+ gbuf.begin(GeomBuffer.TRIANGLE_STRIP);
+
+ if (leftToRight) {
+ // Build triangle strips from left to right
+ delta = startDelta;
+
+ for (j=0; j < i; j++) {
+ vx = r*Math.cos(delta);
+ vz = r*Math.sin(delta);
+
+ gbuf.normal3d( vx*sign, h*sign, vz*sign );
+ gbuf.texCoord2d(0.75 - delta/(2*Math.PI), t);
+ gbuf.vertex3d( vx*radius, h*radius, vz*radius );
+ if (i > 1) {
+ // get previous vertex from buffer
+ index = gbuf.currVertCnt - i2;
+ pt = gbuf.pts[index];
+ norm = gbuf.normals[index];
+ texCoord = gbuf.tcoords[index];
+ // connect with correspondent vertices from previous row
+ gbuf.normal3d(norm.x, norm.y, norm.z);
+ gbuf.texCoord2d(texCoord.x, texCoord.y);
+ gbuf.vertex3d(pt.x, pt.y, pt.z);
+ } else {
+ gbuf.normal3d(0, sign*starth, 0);
+ if (sign > 0) {
+ gbuf.texCoord2d(0.75 - (startDelta + endDelta)/(4*Math.PI),
+ 1.0 - (theta - dt)/Math.PI);
+ } else {
+ gbuf.texCoord2d(0.75 - (startDelta + endDelta)/(4*Math.PI),
+ (theta - dt)/Math.PI);
+ }
+ gbuf.vertex3d( 0, starth*radius, 0);
+
+ }
+ delta += ds;
+ }
+
+ // Put the last vertex in that row,
+ // for numerical accuracy we don't use delta
+ // compute from above.
+ delta = endDelta;
+ vx = r*Math.cos(delta);
+ vz = r*Math.sin(delta);
+ gbuf.normal3d( vx*sign, h*sign, vz*sign );
+ gbuf.texCoord2d(0.75 - delta/(2*Math.PI), t);
+ gbuf.vertex3d( vx*radius, h*radius, vz*radius);
+ } else {
+ delta = endDelta;
+ // Build triangle strips from right to left
+ for (j=i; j > 0; j--) {
+ vx = r*Math.cos(delta);
+ vz = r*Math.sin(delta);
+
+ gbuf.normal3d( vx*sign, h*sign, vz*sign );
+ // Convert texture coordinate back to one
+ // set in previous version
+ gbuf.texCoord2d(0.75 - delta/(2*Math.PI), t);
+ gbuf.vertex3d( vx*radius, h*radius, vz*radius );
+ if (i > 1) {
+ // get previous vertex from buffer
+ index = gbuf.currVertCnt - i2;
+ pt = gbuf.pts[index];
+ norm = gbuf.normals[index];
+ texCoord = gbuf.tcoords[index];
+ gbuf.normal3d(norm.x, norm.y, norm.z);
+ gbuf.texCoord2d(texCoord.x, texCoord.y);
+ gbuf.vertex3d(pt.x, pt.y, pt.z);
+ } else {
+ gbuf.normal3d(0, sign*starth, 0);
+ if (sign > 0) {
+ gbuf.texCoord2d(0.75 - (startDelta + endDelta)/(4*Math.PI),
+ 1.0 - (theta - dt)/Math.PI);
+ } else {
+ gbuf.texCoord2d(0.75 - (startDelta + endDelta)/(4*Math.PI),
+ (theta - dt)/Math.PI);
+ }
+ gbuf.vertex3d( 0, starth*radius, 0);
+
+ }
+ delta -= ds;
+ }
+
+ // Put the last vertex in that row,
+ // for numerical accuracy we don't use delta
+ // compute from above.
+ delta = startDelta;
+ vx = r*Math.cos(delta);
+ vz = r*Math.sin(delta);
+ gbuf.normal3d( vx*sign, h*sign, vz*sign );
+ gbuf.texCoord2d(0.75 - delta/(2*Math.PI), t);
+ gbuf.vertex3d( vx*radius, h*radius, vz*radius );
+
+ }
+
+ gbuf.end();
+
+ if (i < nstep) {
+ theta += dt;
+ } else { // take care of numerical imprecision
+ theta = Math.PI/2;
+ }
+ }
+
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Stripifier.java b/src/classes/share/com/sun/j3d/utils/geometry/Stripifier.java
new file mode 100644
index 0000000..48f5709
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Stripifier.java
@@ -0,0 +1,2535 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+import com.sun.j3d.utils.geometry.GeometryInfo;
+import java.util.LinkedList;
+import java.util.ArrayList;
+import com.sun.j3d.internal.J3dUtilsI18N;
+
+/**
+ * The Stripifier utility will change the primitive of the GeometryInfo
+ * object to Triangle Strips. The strips are made by analyzing the
+ * triangles in the original data and connecting them together.<p>
+ * <p>
+ * Normal Generation should be performed on the GeometryInfo object
+ * <i>before</i> Stripification, for best results. Example:<p>
+ * <p>
+ * <pre>
+ * GeometryInfo gi = new GeometryInfo(TRIANGLE_ARRAY);
+ * gi.setCoordinates(coordinateData);
+ *
+ * NormalGenerator ng = new NormalGenerator();
+ * ng.generateNormals(gi);
+ *
+ * Stripifier st = new Stripifier()
+ * st.stripify(gi);
+ *
+ * Shape3D part = new Shape3D();
+ * part.setAppearance(appearance);
+ * part.setGeometry(gi.getGeometryArray());
+ * </pre>
+ */
+public class Stripifier {
+
+ final boolean DEBUG = false;
+ final boolean CHECK_ORIENT = false;
+
+ static final int EMPTY = -1;
+
+ boolean hasNormals = false;
+ boolean hasTextures = false;
+ int texSetCount = 0;
+ boolean hasColors = false;
+ boolean colorStrips = false;
+
+ StripifierStats stats;
+
+ int[] numNhbrs;
+
+ /**
+ * Indicates to the stripifier to collect statistics on the data
+ */
+ public static final int COLLECT_STATS = 0x01;
+
+ /**
+ * Creates the Stripifier object.
+ */
+ public Stripifier() {
+ }
+
+ /**
+ * Creates the Stripifier object.
+ * @param flags Flags
+ * @since Java 3D 1.2.1
+ */
+ public Stripifier(int flags) {
+ if ((flags & COLLECT_STATS) != 0) {
+ stats = new StripifierStats();
+ }
+ }
+
+ /**
+ * Converts the geometry contained in the GeometryInfo object into an
+ * array of triangle strips.
+ */
+ public void stripify(GeometryInfo gi) {
+ // System.out.println("stripify");
+ long time = System.currentTimeMillis();
+ // setup
+ gi.convertToIndexedTriangles();
+ gi.forgetOldPrim();
+
+ // write out the gi object
+ // System.out.println("write out the object");
+ // gi.writeObj();
+
+ Face[] faces = createFaceArray(gi);
+ Edge[] edges = createEdgeArray(faces);
+ buildAdjacencies(edges, faces);
+
+ // print out the adjacency information
+ if (DEBUG) {
+ for (int i = 0; i < faces.length; i++) {
+ faces[i].printVertices();
+ }
+ System.out.println("");
+ for (int i = 0; i < faces.length; i++) {
+ faces[i].printAdjacency();
+ }
+ System.out.println("");
+ }
+
+ Node[] faceNodes = new Node[faces.length];
+ // Node[] queue = hybridSearch(faces, faceNodes);
+ Node[] queue = dfSearch(faces, faceNodes);
+
+ // print out the queue
+ if (DEBUG) {
+ for (int i = 0; i < queue.length; i++) {
+ queue[i].print();
+ }
+ System.out.println("");
+ }
+
+ // int "pointers" for the numbers of strips and patches from
+ // hamiliton
+ int[] ns = new int[1];
+ int[] np = new int[1];
+ ArrayList hamiltons = hamilton(queue, ns, np);
+ int numStrips = ns[0];
+ int numPatches = np[0];
+
+ // print out the hamiltonians
+ if (DEBUG) {
+ for (int i = 0; i < hamiltons.size(); i++) {
+ System.out.println("Hamiltonian: " + i);
+ ArrayList list = (ArrayList)hamiltons.get(i);
+ for (int j = 0; j < list.size(); j++) {
+ Face face = (Face)list.get(j);
+ face.printVertices();
+ }
+ System.out.println("");
+ }
+ }
+
+ // now make strips out of the hamiltonians
+ ArrayList strips = stripe(hamiltons);
+
+ // print out the strips
+ if (DEBUG) {
+ for (int i = 0; i < strips.size(); i++) {
+ System.out.println("Strip: " + i);
+ Istream istream = (Istream)strips.get(i);
+ for (int j = 0; j < istream.length; j++) {
+ System.out.println("vertex: " + istream.istream[j].index);
+ }
+ System.out.println("");
+ }
+ }
+
+ // concatenate the strips
+ concatenate(strips, faces);
+
+ // print out the new strips
+ if (DEBUG) {
+ System.out.println("");
+ System.out.println("concatenated strips: (" +
+ (strips.size()) + ")");
+ System.out.println("");
+ for (int i = 0; i < strips.size(); i++) {
+ System.out.println("Strip: " + i);
+ Istream istream = (Istream)strips.get(i);
+ for (int j = 0; j < istream.length; j++) {
+ System.out.println("vertex: " + istream.istream[j].index);
+ }
+ System.out.println("");
+ }
+ }
+
+ // put the stripified data into the GeometryInfo object
+ putBackData(gi, strips);
+
+ // System.out.println("time: " + (System.currentTimeMillis()-time));
+ // System.out.println("");
+
+ // add to stats
+ if (stats != null) {
+ stats.updateInfo(System.currentTimeMillis()-time, strips,
+ faces.length);
+ }
+
+ // Stat.printInfo();
+
+ // print out strip count info
+ // System.out.println("numStrips = " + strips.size());
+ // System.out.println("stripCounts:");
+ // int avg = 0;
+ // for (int i = 0; i < strips.size(); i++) {
+ // System.out.print(((Istream)strips.get(i)).length + " ");
+ // avg += ((Istream)strips.get(i)).length;
+ // }
+ // System.out.println("Avg: " + ((double)avg/(double)strips.size()));
+ }
+
+ /**
+ * Prints out statistical information for the stripifier: the number of
+ * original triangles, the number of original vertices, the number of
+ * strips created, the number of vertices, the total number of triangles,
+ * the minimum strip length (in # of tris) the maximum strip length
+ * (in number of tris), the average strip length (in # of tris), the
+ * average number of vertices per triangle, the total time it took to
+ * stripify, and the strip length (how many strips of a given length.
+ * The data is cumulative over all the times the stripifier is called
+ * until the stats are printed, and then they are reset.
+ */
+ // public static void printStats() {
+ // // stats.toString();
+ // }
+
+ /**
+ * Returns the stripifier stats object.
+ * @exception IllegalStateException if the Stripfier has not
+ * been constructed
+ * with the COLLECT_STATS flag
+ * @since Java 3D 1.2.1
+ */
+ public StripifierStats getStripifierStats() {
+ if (stats == null) {
+ throw new IllegalStateException(J3dUtilsI18N.getString("Stripifier0"));
+ }
+ return stats;
+ }
+
+ /**
+ * Creates an array of faces from the geometry in the GeometryInfo object.
+ */
+ Face[] createFaceArray(GeometryInfo gi) {
+ int[] vertices = gi.getCoordinateIndices();
+ int[] normals = gi.getNormalIndices();
+
+ int[][] textures = null;
+ int[] t1 = null;
+ int[] t2 = null;
+ int[] t3 = null;
+ texSetCount = gi.getTexCoordSetCount();
+ if (texSetCount > 0) {
+ hasTextures = true;
+ textures = new int[texSetCount][];
+ for (int i = 0; i < texSetCount; i++) {
+ textures[i] = gi.getTextureCoordinateIndices(i);
+ }
+ t1 = new int[texSetCount];
+ t2 = new int[texSetCount];
+ t3 = new int[texSetCount];
+ } else hasTextures = false;
+
+ int[] colors = gi.getColorIndices();
+ Face[] faces = new Face[vertices.length/3];
+ int n1, n2, n3, c1, c2, c3;
+ Vertex v1, v2, v3;
+ int count = 0;
+ for (int i = 0; i < vertices.length;) {
+ if (normals != null) {
+ // System.out.println("hasNormals");
+ hasNormals = true;
+ n1 = normals[i];
+ n2 = normals[i+1];
+ n3 = normals[i+2];
+ }
+ else {
+ // System.out.println("doesn't have normals");
+ hasNormals = false;
+ n1 = EMPTY;
+ n2 = EMPTY;
+ n3 = EMPTY;
+ }
+ if (hasTextures) {
+ for (int j = 0; j < texSetCount; j++) {
+ t1[j] = textures[j][i];
+ t2[j] = textures[j][(i+1)];
+ t3[j] = textures[j][(i+2)];
+ }
+ }
+ if (colors != null) {
+ hasColors = true;
+ c1 = colors[i];
+ c2 = colors[i+1];
+ c3 = colors[i+2];
+ }
+ else {
+ hasColors = false;
+ c1 = EMPTY;
+ c2 = EMPTY;
+ c3 = EMPTY;
+ }
+ v1 = new Vertex(vertices[i], n1, texSetCount, t1, c1);
+ v2 = new Vertex(vertices[i+1], n2, texSetCount, t2, c2);
+ v3 = new Vertex(vertices[i+2], n3, texSetCount, t3, c3);
+ if (!v1.equals(v2) && !v2.equals(v3) && !v3.equals(v1)) {
+ faces[count] = new Face(count, v1, v2, v3);
+ count++;
+ }
+ i+=3;
+ }
+
+ if (faces.length > count) {
+ Face[] temp = faces;
+ faces = new Face[count];
+ System.arraycopy(temp, 0, faces, 0, count);
+ }
+ return faces;
+ }
+
+ /**
+ * Creates an array of edges from the Face array.
+ */
+ Edge[] createEdgeArray(Face[] faces) {
+ Edge[] edges = new Edge[faces.length*3];
+ Face face;
+ for (int i = 0; i < faces.length; i++) {
+ face = faces[i];
+ edges[i*3] = new Edge(face.verts[0], face.verts[1], face.key);
+ edges[i*3+1] = new Edge(face.verts[1], face.verts[2], face.key);
+ edges[i*3+2] = new Edge(face.verts[2], face.verts[0], face.key);
+ }
+ return edges;
+ }
+
+ /**
+ * Builds the adjacency graph by finding the neighbors of the edges
+ */
+ void buildAdjacencies(Edge[] edges, Face[] faces) {
+ // sortEdges(edges);
+ quickSortEdges(edges, 0, edges.length-1);
+ // int i = 1;
+
+ // set up the edge list of each face
+ Edge edge;
+ Face face;
+ Vertex[] verts;
+ boolean flag;
+ int k;
+ for (int i = 0; i < edges.length; i++) {
+ // edges are kept in order s.t. the ith edge is the opposite
+ // edge of the ith vertex
+ edge = edges[i];
+ face = faces[edge.face];
+ verts = face.verts;
+
+ flag = true;
+ if ((!verts[0].equals(edge.v1)) && (!verts[0].equals(edge.v2))) {
+ face.edges[0] = edge;
+ face.numNhbrs--;
+ flag = false;
+ }
+ else if ((!verts[1].equals(edge.v1)) &&
+ (!verts[1].equals(edge.v2))) {
+ face.edges[1] = edge;
+ face.numNhbrs--;
+ flag = false;
+ }
+ else if ((!verts[2].equals(edge.v1)) &&
+ (!verts[2].equals(edge.v2))) {
+ face.edges[2] = edge;
+ face.numNhbrs--;
+ flag = false;
+ }
+ else {
+ if (DEBUG) System.out.println("error!!! Stripifier.buildAdj");
+ }
+
+ // handle degenerencies
+ if (flag) {
+ Vertex i1;
+ // triangle degenerated to a point
+ if ((edge.v1).equals(edge.v2)) {
+ face.edges[--face.numNhbrs] = edge;
+ }
+ // triangle degenerated to an edge
+ else {
+ if (verts[0].equals(verts[1])) {
+ i1 = verts[1];
+ }
+ else {
+ i1 = verts[2];
+ }
+ if (verts[0].equals(i1) && face.edges[0] == null) {
+ face.edges[0] = edge;
+ face.numNhbrs--;
+ }
+ else if (verts[1].equals(i1) && face.edges[1] == null) {
+ face.edges[1] = edge;
+ face.numNhbrs--;
+ }
+ else {
+ face.edges[2] = edge;
+ face.numNhbrs--;
+ }
+ }
+ }
+ }
+
+ // build the adjacency information by pairing up every two triangles
+ // that share the same edge
+ int i = 0; int j = 0;
+ int j1, j2;
+ while (i < (edges.length-1)) {
+ j = i+1;
+ if (edges[i].equals(edges[j])) {
+ // determine the orientations of the common edge in the two
+ // adjacent triangles. Only set them to be adjacent if they
+ // are opposite
+ j1 = edges[i].face;
+ j2 = edges[j].face;
+ if (j1 != j2) { // set up the two faces as neighbors
+ edge = edges[i];
+ face = faces[j1];
+ k = face.getEdgeIndex(edge);
+ if ((edge.v1.equals(face.verts[(k+1)%3])) &&
+ (edge.v2.equals(face.verts[(k+2)%3]))) {
+ flag = false;
+ }
+ else flag = true;
+
+ edge = edges[j];
+ face = faces[j2];
+ k = face.getEdgeIndex(edge);
+ if ((edge.v1.equals(face.verts[(k+1)%3])) &&
+ (edge.v2.equals(face.verts[(k+2)%3]))) {
+ flag = flag;
+ }
+ else flag = (!flag);
+
+ if (flag) {
+ edges[i].face = j2;
+ edges[j].face = j1;
+ (faces[j1].numNhbrs)++;
+ (faces[j2].numNhbrs)++;
+ j++;
+ }
+ else edges[i].face = EMPTY;
+ }
+ else edges[i].face = EMPTY;
+ }
+ else edges[i].face = EMPTY;
+ i=j;
+ }
+ if (i <= (edges.length-1)) edges[i].face = EMPTY;
+
+ // check, for each face, if it is duplicated. For a face that
+ // neighbors its duplicate in the adjacency graph, it's possible
+ // that two or more of its neighbors are the same (the duplicate).
+ // This will be corrected to avoid introducing redundant faces
+ // later on
+
+ for (i = 0; i < faces.length; i++) {
+ face = faces[i];
+ if (face.numNhbrs == 3) {
+ if ((j1 = face.edges[1].face) == face.edges[0].face) {
+ face.edges[1].face = EMPTY;
+ face.numNhbrs--;
+ faces[j1].counterEdgeDel(face.edges[1]);
+ }
+ if ((j2 = face.edges[2].face) == face.edges[0].face) {
+ face.edges[2].face = EMPTY;
+ face.numNhbrs--;
+ faces[j2].counterEdgeDel(face.edges[2]);
+ }
+ if ((face.edges[1].face != EMPTY) && (j1 == j2)) {
+ face.edges[2].face = EMPTY;
+ face.numNhbrs--;
+ faces[j1].counterEdgeDel(face.edges[2]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sorts the edges using BubbleSort
+ */
+ void sortEdges(Edge[] edges) {
+ int i = edges.length;
+ boolean sorted = false;
+ Edge temp = null;
+ while ((i > 1) && !sorted) {
+ sorted = true;
+ for (int j = 1; j < i; j++) {
+ if (edges[j].lessThan(edges[j-1])) {
+ temp = edges[j-1];
+ edges[j-1] = edges[j];
+ edges[j] = temp;
+ sorted = false;
+ }
+ }
+ i--;
+ }
+ }
+
+ /**
+ * uses quicksort to sort the edges
+ */
+ void quickSortEdges(Edge[] edges, int l, int r) {
+ if (edges.length > 0) {
+ int i = l;
+ int j = r;
+ Edge k = edges[(l+r) / 2];
+
+ do {
+ while (edges[i].lessThan(k)) i++;
+ while (k.lessThan(edges[j])) j--;
+ if (i <= j) {
+ Edge tmp = edges[i];
+ edges[i] = edges[j];
+ edges[j] = tmp;
+ i++;
+ j--;
+ }
+ } while (i <= j);
+
+ if (l < j) quickSortEdges(edges, l, j);
+ if (l < r) quickSortEdges(edges, i, r);
+ }
+ }
+
+ /**
+ * Takes a list of faces as input and performs a hybrid search, a
+ * variated depth first search that returns to the highest level node
+ * not yet fully explored. Returns an array of pointers to the faces
+ * found in order from the search. The faceNodes parameter is an
+ * array of the Nodes created for the faces.
+ */
+ Node[] hybridSearch(Face[] faces, Node[] faceNodes) {
+
+ int numFaces = faces.length;
+ int i = 0, j = 0, k = 0, ind = 0;
+
+ // keep # of faces with certain # of neighbors
+ int[] count = {0, 0, 0, 0};
+
+ // faces sorted by number of neighbors
+ int[] index = new int[numFaces];
+ // the index of a certain face in the sorted array
+ int[] rindex = new int[numFaces];
+
+ // Control list pop up operation
+ boolean popFlag = false;
+
+ // queue of pointers to faces found in search
+ Node[] queue = new Node[numFaces];
+ // root of depth first tree
+ Node source;
+ // for the next node
+ Node nnode;
+ // a face
+ Face face;
+ // starting position for insertion into the list
+ int start = 0;
+ // list for search
+ SortedList dlist;
+
+ // count how many faces have a certain # of neighbors and
+ // create a Node for each face
+ for (i = 0; i < numFaces; i++) {
+ j = faces[i].numNhbrs;
+ count[j]++;
+ faceNodes[i] = new Node(faces[i]);
+ }
+
+ // to help with sorting
+ for (i = 1; i < 4; i++) {
+ count[i] += count[i-1];
+ }
+
+ // decreasing i to make sorting stable
+ for (i = numFaces - 1; i >= 0; i--) {
+ j = faces[i].numNhbrs;
+ count[j]--;
+ index[count[j]] = i;
+ rindex[i] = count[j];
+ }
+
+ // start the hybrid search
+ for (i = 0; i < numFaces; i++) {
+ if (index[i] != EMPTY) {
+ dlist = new SortedList();
+ source = faceNodes[index[i]];
+ source.setRoot();
+ queue[ind] = source;
+ ind++;
+ index[i] = EMPTY;
+
+ while (source != null) {
+ nnode = null;
+ // use the first eligible for continuing search
+ face = source.face;
+ for (j = 0; j < 3; j++) {
+ k = face.getNeighbor(j);
+ if ((k != EMPTY) &&
+ (faceNodes[k].notAccessed())) {
+ nnode = faceNodes[k];
+ break;
+ }
+ }
+
+ if (nnode != null) {
+ // insert the new node
+ nnode.insert(source);
+ if (!popFlag) {
+ start = dlist.sortedInsert(source, start);
+ }
+ else popFlag = false;
+ source = nnode;
+ queue[ind] = source;
+ ind++;
+ index[rindex[k]] = EMPTY;
+ }
+ else {
+ source.processed();
+ source = dlist.pop();
+ popFlag = true;
+ start = 0;
+ }
+ } // while -- does popFlag need to be set to false here?
+ }
+ }
+ return queue;
+ }
+
+ Node[] dfSearch(Face[] faces, Node[] faceNodes) {
+ int numFaces = faces.length;
+ int i = 0, j = 0, k = 0, ind = 0;
+
+ // keep certain # of faces with certain # of neighbors
+ int[] count = {0, 0, 0, 0};
+
+ // faces sorted by # of neighbors
+ int[] index = new int[numFaces];
+ // index of a certain face in the sorted array
+ int[] rindex = new int[numFaces];
+
+ // queue of pointers to faces found in the search
+ Node[] queue = new Node[numFaces];
+ // root of the depth first tree
+ Node source;
+ // the current node
+ Node node;
+ // for the next Node
+ Node nnode;
+ // a face
+ Face face;
+
+ // count how many faces have a certain # of neighbors and create
+ // a Node for each face
+ for (i = 0; i < numFaces; i++) {
+ j = faces[i].numNhbrs;
+ count[j]++;
+ faceNodes[i] = new Node(faces[i]);
+ }
+
+ // to help with sorting
+ for (i = 1; i < 4; i++) count[i] += count[i-1];
+
+ // dec i to make sorting stable
+ for (i = numFaces-1; i >= 0; i--) {
+ j = faces[i].numNhbrs;
+ count[j]--;
+ index[count[j]] = i;
+ rindex[i] = count[j];
+ }
+
+ setNumNhbrs(faces);
+ // start the dfs
+ for (i = 0; i < numFaces; i++) {
+ if (index[i] != EMPTY) {
+ source = faceNodes[index[i]];
+ source.setRoot();
+ queue[ind] = source;
+ ind++;
+ index[i] = EMPTY;
+ node = source;
+
+ do {
+ // if source has been done, stop
+ if ((node == source) && (node.right != null)) break;
+
+ nnode = null;
+ face = node.face;
+
+ // for (j = 0; j < 3; j++) {
+ // if (((k = face.getNeighbor(j)) != EMPTY) &&
+ // (faceNodes[k].notAccessed())) {
+ // nnode = faceNodes[k];
+ // break;
+ // }
+ // }
+
+ k = findNext(node, faceNodes, faces);
+ if (k != EMPTY) nnode = faceNodes[k];
+ if (nnode != null) updateNumNhbrs(nnode);
+
+ if (nnode != null) {
+ // insert new node
+ nnode.insert(node);
+ node = nnode;
+ queue[ind] = node;
+ ind++;
+ index[rindex[k]] = EMPTY;
+ }
+ else {
+ node.processed();
+ node = node.parent;
+ }
+ } while (node != source.parent);
+ }
+ }
+ freeNhbrTable();
+ return queue;
+ }
+
+ int findNext(Node node, Node[] faceNodes, Face[] faces) {
+ Face face = node.face;
+ // this face has no neighbors so return
+ if (face.numNhbrs == 0) return EMPTY;
+
+ int i, j, count;
+ int[] n = new int[3]; // num neighbors of neighboring face
+ int[] ind = {-1, -1, -1}; // neighboring faces
+
+ // find the number of neighbors for each neighbor
+ count = 0;
+ for (i = 0; i < 3; i++) {
+ if (((j = face.getNeighbor(i)) != EMPTY) &&
+ (faceNodes[j].notAccessed())) {
+ ind[count] = j;
+ n[count] = numNhbrs[j];
+ count++;
+ }
+ }
+
+ // this face has no not accessed faces
+ if (count == 0) return EMPTY;
+
+ // this face has only one neighbor
+ if (count == 1) return ind[0];
+
+ if (count == 2) {
+ // if the number of neighbors are the same, try reseting
+ if ((n[0] == n[1]) && (n[0] != 0)) {
+ n[0] = resetNhbr(ind[0], faces, faceNodes);
+ n[1] = resetNhbr(ind[1], faces, faceNodes);
+ }
+ // if one neighbor has fewer neighbors, return that neighbor
+ if (n[0] < n[1]) return ind[0];
+ if (n[1] < n[0]) return ind[1];
+ // neighbors tie. pick the sequential one
+ Node pnode, ppnode;
+ Face pface, ppface;
+ if ((pnode = node.parent) != null) {
+ pface = pnode.face;
+ i = pface.findSharedEdge(face.key);
+ if ((ppnode = pnode.parent) != null) {
+ ppface = ppnode.face;
+ if (pface.getNeighbor((i+1)%3) == ppface.key) {
+ j = pface.verts[(i+2)%3].index;
+ }
+ else {
+ j = pface.verts[(i+1)%3].index;
+ }
+ }
+ else {
+ j = pface.verts[(i+1)%3].index;
+ }
+ i = face.findSharedEdge(ind[0]);
+ if (face.verts[i].index == j) return ind[0];
+ else return ind[1];
+ }
+ else return ind[0];
+ }
+ // three neighbors
+ else {
+ if ((n[0] < n[1]) && (n[0] < n[2])) return ind[0];
+ else if ((n[1] < n[0]) && (n[1] < n[2])) return ind[1];
+ else if ((n[2] < n[0]) && (n[2] < n[1])) return ind[2];
+ else if ((n[0] == n[1]) && (n[0] < n[2])) {
+ if (n[0] != 0) {
+ n[0] = resetNhbr(ind[0], faces, faceNodes);
+ n[1] = resetNhbr(ind[1], faces, faceNodes);
+ }
+ if (n[0] <= n[1]) return ind[0];
+ else return ind[1];
+ }
+ else if ((n[1] == n[2]) && n[1] < n[0]) {
+ if (n[1] != 0) {
+ n[1] = resetNhbr(ind[1], faces, faceNodes);
+ n[2] = resetNhbr(ind[2], faces, faceNodes);
+ }
+ if (n[1] <= n[2]) return ind[1];
+ else return ind[2];
+ }
+ else if ((n[2] == n[0]) && (n[2] < n[1])) {
+ if (n[0] != 0) {
+ n[0] = resetNhbr(ind[0], faces, faceNodes);
+ n[2] = resetNhbr(ind[2], faces, faceNodes);
+ }
+ if (n[0] <= n[2]) return ind[0];
+ else return ind[2];
+ }
+ else {
+ if (n[0] != 0) {
+ n[0] = resetNhbr(ind[0], faces, faceNodes);
+ n[1] = resetNhbr(ind[1], faces, faceNodes);
+ n[2] = resetNhbr(ind[2], faces, faceNodes);
+ }
+ if ((n[0] <= n[1]) && (n[0] <= n[2])) return ind[0];
+ else if (n[1] <= n[2]) return ind[1];
+ else return ind[2];
+ }
+ }
+ }
+
+ void setNumNhbrs(Face[] faces) {
+ int numFaces = faces.length;
+ numNhbrs = new int[numFaces];
+ for (int i = 0; i < numFaces; i++) {
+ numNhbrs[i] = faces[i].numNhbrs;
+ }
+ }
+
+ void freeNhbrTable() {
+ numNhbrs = null;
+ }
+
+ void updateNumNhbrs(Node node) {
+ Face face = node.face;
+ int i;
+ if ((i = face.getNeighbor(0)) != EMPTY) numNhbrs[i]--;
+ if ((i = face.getNeighbor(1)) != EMPTY) numNhbrs[i]--;
+ if ((i = face.getNeighbor(2)) != EMPTY) numNhbrs[i]--;
+ }
+
+ int resetNhbr(int y, Face[] faces, Node[] faceNodes) {
+ int x = EMPTY;
+ Face nface = faces[y];
+ int i;
+ for (int j = 0; j < 3; j++) {
+ if (((i = nface.getNeighbor(j)) != EMPTY) &&
+ (faceNodes[i].notAccessed())) {
+ if ((x == EMPTY) || (x > numNhbrs[i])) x = numNhbrs[i];
+ }
+ }
+ return x;
+ }
+
+ /**
+ * generates hamiltonian strips from the derived binary spanning tree
+ * using the path peeling algorithm to peel off any node wiht double
+ * children in a bottom up fashion. Returns a Vector of strips. Also
+ * return the number of strips and patches in the numStrips and
+ * numPatches "pointers"
+ */
+ ArrayList hamilton(Node[] sTree, int[] numStrips, int[] numPatches) {
+ // the number of nodes in the tree
+ int numNodes = sTree.length;
+ // number of strips
+ int ns = 0;
+ // number of patches
+ int np = 0;
+ // some tree node variables
+ Node node, pnode, cnode;
+ // the Vector of strips
+ ArrayList strips = new ArrayList();
+ // the current strip
+ ArrayList currStrip;
+
+ // the tree nodes are visited in such a bottom-up fashion that
+ // any node is visited prior to its parent
+ for (int i = numNodes - 1; i >= 0; i--) {
+ cnode = sTree[i];
+
+ // if cnode is the root of a tree create a strip
+ if (cnode.isRoot()) {
+ // each patch is a single tree
+ np++;
+ // create a new strip
+ currStrip = new ArrayList();
+ // insert the current node into the list
+ currStrip.add(0, cnode.face);
+
+ // add the left "wing" of the parent node to the strip
+ node = cnode.left;
+ while (node != null) {
+ currStrip.add(0, node.face);
+ node = node.left;
+ }
+
+ // add the right "wing" of the parent node to the strip
+ node = cnode.right;
+ while (node != null) {
+ currStrip.add(currStrip.size(), node.face);
+ node = node.left;
+ }
+
+ // increase the number of strips
+ ns++;
+ // add the strip to the Vector
+ strips.add(currStrip);
+ }
+
+ // if the number of children of this node is 2, create a strip
+ else if (cnode.numChildren == 2) {
+ // if the root has a single child with double children, it
+ // could be left over as a singleton. However, the following
+ // rearrangement reduces the chances
+ pnode = cnode.parent;
+ if (pnode.isRoot() && (pnode.numChildren == 1)) {
+ pnode = cnode.right;
+ if (pnode.left != null) cnode = pnode;
+ else cnode = cnode.left;
+ }
+
+ // handle non-root case
+
+ // remove the node
+ cnode.remove();
+
+ // create a new strip
+ currStrip = new ArrayList();
+ // insert the current node into the list
+ currStrip.add(0, cnode.face);
+
+ // add the left "wing" of cnode to the list
+ node = cnode.left;
+ while (node != null) {
+ currStrip.add(0, node.face);
+ node = node.left;
+ }
+
+ // add the right "wing" of cnode to the list
+ node = cnode.right;
+ while (node != null) {
+ currStrip.add(currStrip.size(), node.face);
+ node = node.left;
+ }
+
+ // increase the number of strips
+ ns++;
+ // add the strip to the Vector
+ strips.add(currStrip);
+ }
+ }
+
+ // put the ns and np in the "pointers to return
+ numStrips[0] = ns;
+ numPatches[0] = np;
+
+ // return the strips
+ return strips;
+ }
+
+ /**
+ * creates the triangle strips
+ */
+ ArrayList stripe(ArrayList strips) {
+ int numStrips = strips.size(); // the number of strips
+ int count; // where we are in the hamiltonian
+ Face face; // the face we are adding to the stream
+ Face prev; // the previous face added to the stream
+ boolean done; // whether we are done with the current strip
+ boolean cont; // whether we should continue the current stream
+ ArrayList currStrip; // the current hamiltonian
+ Istream currStream; // the stream we are building
+ ArrayList istreams = new ArrayList(); // the istreams to return
+ boolean ccw = true;; // counter-clockwise
+ int share; // the shared edge
+ Vertex[] buf = new Vertex[4]; // a vertex array to start the stream
+
+ // create streams for each hamiltonian
+ for (int i = 0; i < numStrips; i++) {
+ currStrip = (ArrayList)strips.get(i);
+ count = 0;
+ done = false;
+ face = getNextFace(currStrip, count++);
+
+ // while we are not done with the current hamiltonian
+ while (!done) {
+ cont = true;
+
+ // if the current face is the only one left in the current
+ // hamiltonian
+ if (stripDone(currStrip, count)) {
+ // create a new istream with the current face
+ currStream = new Istream(face.verts, 3, false);
+ // set the head of the strip to this face
+ currStream.head = face.key;
+ done = true;
+ // since we are done with the strip, set the tail to this
+ // face
+ currStream.tail = face.key;
+ }
+
+ else {
+ prev = face;
+ face = getNextFace(currStrip, count++);
+
+ // put the prev vertices in the correct order
+ // to add the next tri on
+ share = prev.findSharedEdge(face.key);
+ buf[0] = prev.verts[share];
+ buf[1] = prev.verts[(share+1)%3];
+ buf[2] = prev.verts[(share+2)%3];
+
+ // find the fourth vertex
+ if (CHECK_ORIENT) {
+ // check for clockwise orientation
+ if (checkOrientCWSeq(buf[2], buf[1], face)) {
+ share = face.findSharedEdge(prev.key);
+ buf[3] = face.verts[share];
+ currStream = new Istream(buf, 4, false);
+ // set the head of this strip to the prev face
+ currStream.head = prev.key;
+ // if this was the last tri in the strip, then
+ // we are done
+ if (stripDone(currStrip, count)) {
+ done = true;
+ // set the tail for the strip to current face
+ currStream.tail = face.key;
+ }
+ }
+ else {
+ cont = false;
+ currStream = new Istream(buf, 3, false);
+ // set the head to the prev face
+ currStream.head = prev.key;
+ // since we are not continuing, set
+ // the tail to prev also
+ currStream.tail = prev.key;
+ }
+
+ // orientation starts counter-clockwise for 3rd face
+ ccw = true;
+ }
+ else {
+ share = face.findSharedEdge(prev.key);
+ buf[3] = face.verts[share];
+ currStream = new Istream(buf, 4, false);
+ // set the head of this strip to the prev face
+ currStream.head = prev.key;
+ // if this was the last tri in the strip, then
+ // we are done
+ if (stripDone(currStrip, count)) {
+ done = true;
+ // set the tail for the strip to current face
+ currStream.tail = face.key;
+ }
+ }
+
+ // while continue and the strip isn't finished
+ // add more faces to the stream
+ while (cont && !stripDone(currStrip, count)) {
+ prev = face;
+ face = getNextFace(currStrip, count++);
+ share = face.findSharedEdge(prev.key);
+
+ // if we can add the face without adding any
+ // zero area triangles
+ if (seq(currStream, face, share)) {
+ if (CHECK_ORIENT) {
+ // if we can add the next face with the correct
+ // orientation
+ if (orientSeq(ccw, currStream, face)) {
+ // append the vertex opposite the
+ //shared edge
+ currStream.append(face.verts[share]);
+ // next face must have opposite orientation
+ ccw = (!ccw);
+ // if this was the last tri in the
+ //strip, then we are done
+ if (stripDone(currStrip, count)) {
+ done = true;
+ // since we are done with this strip,
+ // set the tail to the current face
+ currStream.tail = face.key;
+ }
+ }
+ // if we cannot add the face with the correct
+ // orientation, do not continue with this
+ // stream
+ else {
+ cont = false;
+ // since we cannot continue with this strip
+ // set the tail to prev
+ currStream.tail = prev.key;
+ }
+ }
+ else {
+ // append the vertex opposite the
+ //shared edge
+ currStream.append(face.verts[share]);
+ // if this was the last tri in the
+ //strip, then we are done
+ if (stripDone(currStrip, count)) {
+ done = true;
+ // since we are done with this strip,
+ // set the tail to the current face
+ currStream.tail = face.key;
+ }
+ }
+ }
+
+ // need zero area tris to add continue the strip
+ else {
+ if (CHECK_ORIENT) {
+ // check the orientation for adding a zero
+ // area tri and this face
+ if (orientZAT(ccw, currStream, face)) {
+ // swap the end of the current stream to
+ // add a zero area triangle
+ currStream.swapEnd();
+ // append the vertex opposite the
+ // shared edge
+ currStream.append(face.verts[share]);
+ // if this was the last tri in the
+ // strip then we are done
+ if (stripDone(currStrip, count)) {
+ done = true;
+ // set the tail because we are done
+ currStream.tail = face.key;
+ }
+ }
+ // if we cannot add the face with the correct
+ // orientation, do not continue with this
+ // stream
+ else {
+ cont = false;
+ // since we cannot continue with this face,
+ // set the tail to the prev face
+ currStream.tail = prev.key;
+ }
+ }
+ else {
+ // swap the end of the current stream to
+ // add a zero area triangle
+ currStream.swapEnd();
+ // append the vertex opposite the
+ // shared edge
+ currStream.append(face.verts[share]);
+ // if this was the last tri in the
+ // strip then we are done
+ if (stripDone(currStrip, count)) {
+ done = true;
+ // set the tail because we are done
+ currStream.tail = face.key;
+ }
+ }
+ }
+ } // while (cont && !stripDone)
+ } // else
+
+ // add the current strip to the strips to be returned
+ istreams.add(currStream);
+ } // while !done
+ } // for each hamiltonian
+ return istreams;
+ } // stripe
+
+ boolean stripDone(ArrayList strip, int count) {
+ if (count < strip.size()) {
+ return false;
+ }
+ else return true;
+ }
+
+ boolean seq(Istream stream, Face face, int share) {
+ int length = stream.length;
+ Vertex v1 = face.edges[share].v1;
+ Vertex v2 = face.edges[share].v2;
+ Vertex last = stream.istream[length-1];
+ Vertex prev = stream.istream[length-2];
+ if (((v1.equals(prev)) && (v2.equals(last))) ||
+ ((v1.equals(last)) && (v2.equals(prev)))) {
+ return true;
+ }
+ else return false;
+ }
+
+ boolean orientSeq(boolean ccw, Istream stream, Face face) {
+ int length = stream.length;
+ Vertex last = stream.istream[length-1];
+ Vertex prev = stream.istream[length-2];
+ if ((ccw && checkOrientCCWSeq(last, prev, face)) ||
+ ((!ccw) && checkOrientCWSeq(last, prev, face))) {
+ return true;
+ }
+ else return false;
+ }
+
+ boolean orientZAT(boolean ccw, Istream stream, Face face) {
+ int length = stream.length;
+ Vertex last = stream.istream[length-1];
+ Vertex swap = stream.istream[length-3];
+ if ((ccw && checkOrientCWSeq(last, swap, face)) ||
+ ((!ccw) && checkOrientCCWSeq(last, swap, face))) {
+ return true;
+ }
+ else return false;
+ }
+
+ boolean checkOrientCWSeq(Vertex last, Vertex prev, Face face) {
+ System.out.println("checkOrientCWSeq");
+ System.out.println("last = " + last.index);
+ System.out.println("prev = " + prev.index);
+ System.out.print("face = ");
+ face.printVertices();
+ if (last.equals(face.verts[0])) {
+ if (!prev.equals(face.verts[1])) {
+ if (DEBUG) System.out.println("ORIENTATION PROBLEM!");
+ return false;
+ }
+ }
+ else if (last.equals(face.verts[1])) {
+ if (!prev.equals(face.verts[2])) {
+ if (DEBUG) System.out.println("ORIENTATION PROBLEM!");
+ return false;
+ }
+ }
+ else if (last.equals(face.verts[2])) {
+ if (!prev.equals(face.verts[0])) {
+ if (DEBUG) System.out.println("ORIENTATION PROBLEM!");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ boolean checkOrientCCWSeq(Vertex last, Vertex prev, Face face) {
+ System.out.println("checkOrientCCWSeq");
+ System.out.println("last = " + last.index);
+ System.out.println("prev = " + prev.index);
+ System.out.print("face = ");
+ face.printVertices();
+ if (prev.equals(face.verts[0])) {
+ if (!last.equals(face.verts[1])) {
+ System.out.println("ORIENTATION PROBLEM!");
+ return false;
+ }
+ }
+ else if (prev.equals(face.verts[1])) {
+ if (!last.equals(face.verts[2])) {
+ System.out.println("ORIENTATION PROBLEM!");
+ return false;
+ }
+ }
+ else if (prev.equals(face.verts[2])) {
+ if (!last.equals(face.verts[0])) {
+ System.out.println("ORIENTATION PROBLEM!");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ Face getNextFace(ArrayList currStrip, int index) {
+ if (currStrip.size() > index) return (Face)currStrip.get(index);
+ else return null;
+ }
+
+ /**
+ * joins tristrips if their end triangles neighbor each other. The
+ * priority is performed in three stages: strips are concatenated to
+ * save 2, 1, or no vertices
+ */
+ void concatenate(ArrayList strips, Face[] faces) {
+ int numFaces = faces.length;
+ int[] faceTable = new int[numFaces];
+ Istream strm;
+
+ // initialize the face table to empty
+ for (int i = 0; i < numFaces; i++) {
+ faceTable[i] = EMPTY;
+ }
+
+ // set up the faceTable so that a face index relates to a strip
+ // that owns the face as one of its end faces
+ for (int i = 0; i < strips.size(); i++) {
+ strm = (Istream)strips.get(i);
+ faceTable[strm.head] = i;
+ faceTable[strm.tail] = i;
+ }
+
+ if (DEBUG) {
+ System.out.println("");
+ System.out.println("faceTable:");
+ for (int i = 0; i < faceTable.length; i++) {
+ System.out.println(faceTable[i]);
+ }
+ System.out.println("");
+ }
+
+ reduceCostByTwo(strips, faces, faceTable);
+ reduceCostByOne(strips, faces, faceTable);
+ reduceCostByZero(strips, faces, faceTable);
+ }
+
+ /**
+ * find all the links that reduce the cost by 2
+ */
+ void reduceCostByTwo(ArrayList strips, Face[] faces, int[] faceTable) {
+ // System.out.println("reduceCostByTwo");
+ // number of faces in the face array
+ int numFaces = faces.length;
+ // possible adjacent strips
+ int id, id1, id2;
+ // Istreams
+ Istream strm, strm1;
+ // the length of the Istrem
+ int len, len1;
+ // vertex sequences for tristrips
+ Vertex[] seq, seq1;
+ // a face
+ Face face;
+ // the list of vertices for the face
+ Vertex[] verts;
+ // used to syncronize the orientation
+ boolean sync, sync1;
+ // a swap variable
+ Vertex swap;
+
+ for (int i = 0; i < numFaces; i++) {
+ id = faceTable[i];
+ if (id != EMPTY) {
+ sync = false; sync1 = false;
+ strm = (Istream)strips.get(id);
+ len = strm.length;
+ seq = strm.istream;
+ face = faces[i];
+ verts = face.verts;
+
+ // sequential strips
+ if (!strm.fan) {
+
+ // a singleton strip
+ if (len == 3) {
+
+ // check all three neighbors
+ for (int j = 0; j < 3; j++) {
+ int k = face.getNeighbor(j);
+ if ((k != EMPTY) &&
+ ((id1 = faceTable[k]) != EMPTY) &&
+ (id1 != id)) {
+ // reassign the sequence
+ seq[0] = verts[j];
+ seq[1] = verts[(j+1)%3];
+ seq[2] = verts[(j+2)%3];
+
+ // the neighboring stream
+ strm1 = (Istream)strips.get(id1);
+ len1 = strm1.length;
+ if (k != strm1.head) {
+ strm1.invert();
+ // if the length is odd set sync1 to true
+ if ((len1 % 2) != 0) sync1 = true;
+ }
+ seq1 = strm1.istream;
+
+ // append a singleton strip
+ if (len1 == 3) {
+ // System.out.println("reduce2");
+ int m = faces[k].findSharedEdge(i);
+ strm.append(faces[k].verts[m]);
+ strm1.length = 0;
+ strm1.istream = null;
+ strm.tail = k;
+ faceTable[k] = id;
+ i--;
+ break;
+ }
+
+ // append a strip of length over 2
+ else {
+ if ((len1 == 4) &&
+ (seq[1].index == seq1[0].index) &&
+ (seq[2].index == seq1[2].index)) {
+
+ // swap seq1[1] and seq1[2] so that
+ // seq[1] == seq1[0] and
+ // seq[1] == seq1[1]
+ swap = seq1[1];
+ seq1[1] = seq1[2];
+ seq1[2] = swap;
+ }
+
+ // see if we can join the strips
+ if ((seq[1].index == seq1[0].index) &&
+ (seq[2].index == seq1[1].index)) {
+ // System.out.println("reduce2");
+ // add the stream in
+ strm.addStream(strm1);
+ faceTable[k] = EMPTY;
+ faceTable[strm.tail] = id;
+
+ i--;
+ break;
+ }
+ else if (sync1) {
+ strm1.invert();
+ sync1 = false;
+ }
+ }
+ }
+ }
+ }
+
+ // not a singleton strip
+
+ // can append a stream where the current face is the tail
+ // or is an even length so we can invert it
+ else if ((i == strm.tail) || ((len % 2) == 0)) {
+ // if the current face isn't the tail, then
+ // have to invert the strip
+ if (i != strm.tail) {
+ strm.invert();
+ seq = strm.istream;
+ }
+
+ // System.out.println("seq.length = " + seq.length);
+ // System.out.println("len = " + len);
+ // System.out.print("seq = ");
+ // for (int l = 0; l < seq.length; l++) {
+ // if (seq[l] == null) System.out.print(" null");
+ // else System.out.print(" " + seq[l].index);
+ // }
+ // System.out.println("");
+
+ swap = seq[len - 3];
+
+ // find the neighboring strip
+ int m = EMPTY;
+ if (verts[0].index == swap.index) m = 0;
+ else if (verts[1].index == swap.index) m = 1;
+ else if (verts[2].index == swap.index) m = 2;
+ if (m == EMPTY) {
+ if (DEBUG) System.out.println("problem finding neighbor strip");
+ }
+ int j = face.getNeighbor(m);
+ if (j == EMPTY) id1 = j;
+ else id1 = faceTable[j];
+ if ((id1 != EMPTY) &&
+ (((Istream)strips.get(id1)).fan !=
+ strm.fan)) {
+ id1 = EMPTY;
+ }
+
+ if ((id1 != EMPTY) && (id1 != id)) {
+ strm1 = (Istream)strips.get(id1);
+ len1 = strm1.length;
+
+ // if the shared face isn't the head, invert
+ // the stream
+ if (j != strm1.head) {
+ strm1.invert();
+ // set the sync var if the length is odd
+ if ((len1 % 2) != 0) sync1 = true;
+ }
+ seq1 = strm1.istream;
+
+ // append a singleton strip
+ if (len1 == 3) {
+ // System.out.println("reduce2");
+ m = faces[j].findSharedEdge(i);
+ strm.append(faces[j].verts[m]);
+ strm1.length = 0;
+ strm1.istream = null;
+ strm.tail = j;
+ faceTable[i] = EMPTY;
+ faceTable[j] = id;
+ }
+
+ // append a non-singleton strip
+ else {
+ if ((len1 == 4) &&
+ (seq[len-2].index == seq1[0].index) &&
+ (seq[len-1].index == seq1[2].index)) {
+
+ // swap seq1[1] and seq1[2] so that
+ // seq[len-2] == seq1[0] and
+ // seq[len-1] == seq1[1]
+ swap = seq1[1];
+ seq1[1] = seq1[2];
+ seq1[2] = swap;
+ }
+
+ // see if we can append the strip
+ if ((seq[len-2].index == seq1[0].index) &&
+ (seq[len-1].index == seq1[1].index)) {
+ // System.out.println("reduce2");
+ strm.addStream(strm1);
+ faceTable[i] = EMPTY;
+ faceTable[strm.tail] = id;
+ faceTable[j] = EMPTY;
+ }
+ else if (sync1) strm1.invert();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * find all links that reduce cost by 1
+ */
+ void reduceCostByOne(ArrayList strips, Face[] faces, int[] faceTable) {
+ // System.out.println("reduceCostByOne");
+ // number of faces in the face array
+ int numFaces = faces.length;
+ // possible adjacent strips
+ int id, id1, id2;
+ // Istreams
+ Istream strm, strm1;
+ // the length of the Istream
+ int len, len1;
+ // vertex sequences for tristrips
+ Vertex[] seq, seq1;
+ // a face
+ Face face;
+ // the list of vertices for the face
+ Vertex[] verts;
+ // used to synchronize the orientation
+ boolean sync, sync1;
+ // a swap variable
+ Vertex swap;
+
+ for (int i = 0; i < numFaces; i++) {
+ id = faceTable[i];
+ if ((id != EMPTY) && !((Istream)strips.get(id)).fan) {
+ sync = false;
+ strm = (Istream)strips.get(id);
+ seq = strm.istream;
+ face = faces[i];
+ verts = face.verts;
+ len = strm.length;
+
+ // a singleton strip
+ if (len == 3) {
+
+ // consider the three neighboring triangles
+ for (int j = 0; j < 3; j++) {
+ int k = face.getNeighbor(j);
+ if ((k != EMPTY) &&
+ ((id1 = faceTable[k]) != EMPTY) &&
+ (id1 != id) &&
+ (!((Istream)strips.get(id1)).fan)) {
+
+ // reassign the sequence
+ seq[0] = verts[j];
+ seq[1] = verts[(j+1)%3];
+ seq[2] = verts[(j+2)%3];
+
+ // the neighboring stream
+ strm1 = (Istream)strips.get(id1);
+ len1 = strm1.length;
+ if (k != strm1.head) {
+ strm1.invert();
+ if ((len1 % 2) != 0) sync = true;
+ }
+ seq1 = strm1.istream;
+
+ // see if we can join the strips
+
+ if ((len1 == 4) &&
+ (((seq[1].index == seq1[2].index) &&
+ (seq[2].index == seq1[0].index)) ||
+ ((seq[1].index == seq1[0].index) &&
+ (seq[2].index == seq1[2].index)))) {
+ swap = seq1[1];
+ seq1[1] = seq1[2];
+ seq1[2] = swap;
+ }
+
+ if ((seq[1].index == seq1[0].index) &&
+ (seq[2].index == seq1[1].index)) {
+ // System.out.println("reduce1");
+ strm.addStream(strm1);
+ faceTable[k] = EMPTY;
+ faceTable[strm.tail] = id;
+ i--;
+ break;
+ }
+
+ if ((seq[1].index == seq1[1].index) &&
+ (seq[2].index == seq1[0].index)) {
+ // System.out.println("reduce1");
+ strm.append(seq1[1]);
+ strm.addStream(strm1);
+ faceTable[k] = EMPTY;
+ faceTable[strm.tail] = id;
+ i--;
+ break;
+ }
+
+ if ((seq[1].index == seq1[0].index) &&
+ (seq[2].index == seq1[2].index)) {
+ // System.out.println("reduce1");
+ seq1[0] = seq1[2];
+ strm.append(seq1[1]);
+ strm.addStream(strm1);
+ faceTable[k] = EMPTY;
+ faceTable[strm.tail] = id;
+ i--;
+ break;
+ }
+
+ if (sync) {
+ strm1.invert();
+ sync = false;
+ }
+ }
+ }
+ }
+
+ // non-singleton strip
+ else if ((i == strm.tail) || ((len % 2) == 0)) {
+
+ // make sure the face i ends the id-th strip
+ if (i != strm.tail) {
+ strm.invert();
+ seq = strm.istream;
+ }
+
+ swap = seq[len-3];
+
+ // find the neighboring strip
+ int m = EMPTY;
+ if (verts[0].index == swap.index) m = 0;
+ else if (verts[1].index == swap.index) m = 1;
+ else if (verts[2].index == swap.index) m = 2;
+ if (m == EMPTY) {
+ if (DEBUG) System.out.println("problem finding neighbor strip");
+ }
+ int j = face.getNeighbor(m);
+ if (j == EMPTY) id1 = j;
+ else id1 = faceTable[j];
+ if ((id1 != EMPTY) &&
+ (((Istream)strips.get(id1)).fan != strm.fan)) {
+ id1 = EMPTY;
+ }
+
+ // find another neighboring strip
+ swap = seq[len-2];
+ m = EMPTY;
+ if (verts[0].index == swap.index) m = 0;
+ else if (verts[1].index == swap.index) m = 1;
+ else if (verts[2].index == swap.index) m = 2;
+ if (m == EMPTY) {
+ if (DEBUG) System.out.println("problem finding neighbor strip.");
+ }
+ int k = face.getNeighbor(m);
+ if (k == EMPTY) id2 = k;
+ else id2 = faceTable[k];
+ if ((id2 != EMPTY) &&
+ (((Istream)strips.get(id2)).fan != strm.fan)) {
+ id2 = EMPTY;
+ }
+
+ // consider strip id1
+ boolean success = false;
+ if ((id1 != EMPTY) && (id1 != id)) {
+ strm1 = (Istream)strips.get(id1);
+ len1 = strm1.length;
+ if (j != strm1.head) {
+ strm1.invert();
+ if ((len1 % 2) != 0) sync = true;
+ }
+ seq1 = strm1.istream;
+
+ if ((len1 == 4) &&
+ (((seq[len-2].index == seq1[2].index) &&
+ (seq[len-1].index == seq1[0].index)) ||
+ (seq[len-2].index == seq1[0].index) &&
+ (seq[len-1].index == seq1[2].index))) {
+ swap = seq1[1];
+ seq1[1] = seq1[2];
+ seq1[2] = swap;
+ }
+
+ // find matches
+ if ((seq[len-2].index == seq1[0].index) &&
+ (seq[len-1].index == seq1[1].index)) {
+ // System.out.println("reduce1");
+ strm.addStream(strm1);
+ faceTable[i] = EMPTY;
+ faceTable[strm.tail] = id;
+ faceTable[j] = EMPTY;
+ success = true;
+ }
+
+ else if ((seq[len-2].index == seq1[1].index) &&
+ (seq[len-1].index == seq1[0].index)) {
+ // System.out.println("reduce1");
+ strm.append(seq1[1]);
+ strm.addStream(strm1);
+ faceTable[i] = EMPTY;
+ faceTable[strm.tail] = id;
+ faceTable[j] = EMPTY;
+ success = true;
+ }
+
+ else if ((seq[len-2].index == seq1[0].index) &&
+ (seq[len-1].index == seq1[2].index)) {
+ // System.out.println("reduce1");
+ seq1[0] = seq1[2];
+ strm.append(seq1[1]);
+ strm.addStream(strm1);
+ faceTable[i] = EMPTY;
+ faceTable[strm.tail] = id;
+ faceTable[j] = EMPTY;
+ success = true;
+ }
+ else if (sync) {
+ strm1.invert();
+ sync = false;
+ }
+ }
+
+ // now consider strip id2
+ if (!success &&
+ (id2 != EMPTY) && (id2 != id)) {
+ strm1 = (Istream)strips.get(id2);
+ len1 = strm1.length;
+ if (k != strm1.head) {
+ strm1.invert();
+ if ((len1 % 2) != 0) sync = true;
+ }
+ seq1 = strm1.istream;
+
+ if ((len1 == 4) &&
+ (seq[len-3].index == seq1[0].index) &&
+ (seq[len-1].index == seq1[2].index)) {
+ swap = seq1[1];
+ seq1[1] = seq1[2];
+ seq1[2] = swap;
+ }
+
+ // find matches
+
+ if ((seq[len-3].index == seq1[0].index) &&
+ (seq[len-1].index == seq1[1].index)) {
+ // System.out.println("reduce1");
+ strm.swapEnd();
+ strm.addStream(strm1);
+ faceTable[i] = EMPTY;
+ faceTable[strm.tail] = id;
+ faceTable[k] = EMPTY;
+ success = true;
+ }
+ if (!success && sync) strm1.invert();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * find all the links that reduce the cost by 0
+ */
+ void reduceCostByZero(ArrayList strips, Face[] faces, int[] faceTable) {
+ // System.out.println("reduceCostByZero");
+ // number of faces in the face array
+ int numFaces = faces.length;
+ // possible adjacent strips
+ int id, id1, id2;
+ // Istreams
+ Istream strm, strm1;
+ // the length of the Istream
+ int len, len1;
+ // vertex sequences for tristrips
+ Vertex[] seq, seq1;
+ // a face
+ Face face;
+ // the list of vertices for the face
+ Vertex[] verts;
+ // used to synchronize the orientation
+ boolean sync, sync1;
+ // a swap variable
+ Vertex swap;
+
+ for (int i = 0; i < numFaces; i++) {
+ id = faceTable[i];
+ if ((id != EMPTY) && !((Istream)strips.get(id)).fan) {
+ sync = false;
+ strm = (Istream)strips.get(id);
+ seq = strm.istream;
+ len = strm.length;
+ face = faces[i];
+ verts = face.verts;
+
+ if (len == 3) {
+ for (int j = 0; j < 3; j++) {
+ int k = face.getNeighbor(j);
+ if ((k != EMPTY) && ((id1 = faceTable[k]) != EMPTY) &&
+ (id1 != id) &&
+ !((Istream)strips.get(id1)).fan) {
+ // reassign the sequence
+ seq[0] = verts[j];
+ seq[1] = verts[(j+1)%3];
+ seq[2] = verts[(j+2)%3];
+
+ // the neighboring stream
+ strm1 = (Istream)strips.get(id1);
+ len1 = strm1.length;
+ if (k != strm1.head) {
+ strm1.invert();
+ if ((len1 % 2) != 0) sync = true;
+ }
+ seq1 = strm1.istream;
+
+ // see if we can join the strips
+ if ((seq[1].index == seq1[2].index) &&
+ (seq[2].index == seq1[0].index)) {
+ // System.out.println("reduce0");
+ seq1[0] = seq1[2];
+ strm.append(seq1[0]);
+ strm.append(seq1[1]);
+ strm.addStream(strm1);
+ faceTable[k] = EMPTY;
+ faceTable[strm.tail] = id;
+ i--;
+ break;
+ }
+ else if (sync) {
+ strm1.invert();
+ sync = false;
+ }
+ }
+ }
+ }
+ else if ((i == strm.tail) || ((len % 2) == 0)) {
+ if (i != strm.tail) {
+ strm.invert();
+ seq = strm.istream;
+ }
+
+ swap = seq[len-3];
+
+ // find neighboring strip
+ int m = EMPTY;
+ if (verts[0].index == swap.index) m = 0;
+ else if (verts[1].index == swap.index) m = 1;
+ else if (verts[2].index == swap.index) m = 2;
+ if (m == EMPTY) {
+ if (DEBUG) System.out.println("problem finding neighbor strip");
+ }
+ int j = face.getNeighbor(m);
+ if (j == EMPTY) id1 = j;
+ else id1 = faceTable[j];
+ if ((id1 != EMPTY) &&
+ (((Istream)strips.get(id1)).fan != strm.fan)) {
+ id1 = EMPTY;
+ }
+
+ // find another neighboring strip
+ swap = seq[len-2];
+ m = EMPTY;
+ if (verts[0].index == swap.index) m = 0;
+ else if (verts[1].index == swap.index) m = 1;
+ else if (verts[2].index == swap.index) m = 2;
+ if (m == EMPTY) {
+ if (DEBUG) System.out.println("problem finding neighbor strip.");
+ }
+ int k = face.getNeighbor(m);
+ if (k == EMPTY) id2 = k;
+ else id2 = faceTable[k];
+ if ((id2 != EMPTY) &&
+ (((Istream)strips.get(id2)).fan != strm.fan)) {
+ id2 = EMPTY;
+ }
+
+ // consider strip id1
+ boolean success = false;
+ if ((id1 != EMPTY) && (id1 != id)) {
+ strm1 = (Istream)strips.get(id1);
+ len1 = strm1.length;
+ if (j != strm1.head) {
+ strm1.invert();
+ if ((len1 % 2) != 0) sync = true;
+ }
+ seq1 = strm1.istream;
+
+ // find matches
+ if ((seq[len-2].index == seq1[2].index) &&
+ (seq[len-1].index == seq1[0].index)) {
+ // System.out.println("reduce0");
+ seq1[0] = seq1[2];
+ strm.append(seq1[0]);
+ strm.append(seq1[1]);
+ strm.addStream(strm1);
+ faceTable[i] = EMPTY;
+ faceTable[strm.tail] = id;
+ faceTable[j] = EMPTY;
+ success = true;
+ }
+ else if (sync) {
+ strm1.invert();
+ sync = false;
+ }
+ }
+
+ // consider strip id2
+ if (!success && (id2 != EMPTY) && (id2 != id)) {
+ strm1 = (Istream)strips.get(id2);
+ len1 = strm1.length;
+ if (k != strm1.head) {
+ strm1.invert();
+ if ((len1 % 2) != 0) sync = true;
+ }
+ seq1 = strm1.istream;
+
+ if ((len1 == 4) &&
+ (((seq[len-3].index == seq1[2].index) &&
+ (seq[len-1].index == seq1[0].index)) ||
+ ((seq[len-3].index == seq1[0].index) &&
+ (seq[len-1].index == seq1[2].index)))) {
+
+ swap = seq1[1];
+ seq1[1] = seq1[2];
+ seq1[2] = swap;
+ }
+
+ // find matches
+ if ((seq[len-3].index == seq1[1].index) &&
+ (seq[len-1].index == seq1[0].index)) {
+ // System.out.println("reduce0");
+ strm.swapEnd();
+ strm.append(seq1[1]);
+ strm.addStream(strm1);
+ faceTable[i] = EMPTY;
+ faceTable[strm.tail] = id;
+ faceTable[k] = EMPTY;
+ }
+ else if ((seq[len-3].index == seq1[0].index) &&
+ (seq[len-1].index == seq1[2].index)) {
+ // System.out.println("reduce0");
+ seq1[0] = seq1[2];
+ strm.swapEnd();
+ strm.append(seq1[1]);
+ strm.addStream(strm1);
+ faceTable[i] = EMPTY;
+ faceTable[strm.tail] = id;
+ faceTable[k] = EMPTY;
+ }
+ else if ((seq[len-3].index == seq1[0].index) &&
+ (seq[len-1].index == seq1[1].index)) {
+ // System.out.println("reduce0");
+ strm.swapEnd();
+ strm.addStream(strm1);
+ faceTable[i] = EMPTY;
+ faceTable[strm.tail] = id;
+ faceTable[k] = EMPTY;
+ }
+ else if (sync) strm1.invert();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * puts the stripified data back into the GeometryInfo object
+ */
+ void putBackData(GeometryInfo gi, ArrayList strips) {
+ int[] tempStripCounts = new int[strips.size()];
+ int ciSize = 0;
+ int stripLength;
+ for (int i = 0; i < strips.size();) {
+ stripLength = ((Istream)strips.get(i)).length;
+ if (stripLength != 0) {
+ tempStripCounts[i] = stripLength;
+ ciSize += stripLength;
+ i++;
+ }
+ else {
+ strips.remove(i);
+ }
+ }
+ if (ciSize > 3) {
+ gi.setPrimitive(gi.TRIANGLE_STRIP_ARRAY);
+ int[] stripCounts = new int[strips.size()];
+ System.arraycopy(tempStripCounts, 0, stripCounts, 0, strips.size());
+ gi.setStripCounts(stripCounts);
+
+ // create one array with all the strips
+ int[] coords = new int[ciSize];
+
+ // create arrays for normals, textures and colors if necessary
+ int[] normals = null;
+ int[][] textures = null;
+ int[] colors = null;
+ javax.vecmath.Color3b[] stripColors = null;
+ if (hasNormals) normals = new int[ciSize];
+ if (hasTextures) {
+ textures = new int[texSetCount][ciSize];
+ }
+ if (hasColors) colors = new int[ciSize];
+ if (colorStrips) {
+ stripColors = new javax.vecmath.Color3b[ciSize];
+ colors = new int[ciSize];
+ }
+ int count = 0;
+ Istream currStrip;
+ for (int i = 0; i < strips.size(); i++) {
+ currStrip = (Istream)strips.get(i);
+
+ if (currStrip.length < 3) {
+ throw new RuntimeException("currStrip.length = " +
+ currStrip.length);
+ }
+
+ java.awt.Color stripColor = null;
+ if (colorStrips) {
+ int r = ((int)(Math.random()*1000))%255;
+ int g = ((int)(Math.random()*1000))%255;
+ int b = ((int)(Math.random()*1000))%255;
+ stripColor = new java.awt.Color(r, g, b);
+ }
+
+ for (int j = 0; j < currStrip.length; j++) {
+ coords[count] = currStrip.istream[j].index;
+ if (hasNormals) normals[count] = currStrip.istream[j].normal;
+ if (hasTextures) {
+ for (int k = 0; k < texSetCount; k++) {
+ textures[k][count] =
+ currStrip.istream[j].texture[k];
+ }
+ }
+ if (hasColors) colors[count] = currStrip.istream[j].color;
+ if (colorStrips) stripColors[count] =
+ new javax.vecmath.Color3b(stripColor);
+ count++;
+ }
+ }
+ gi.setCoordinateIndices(coords);
+ if (hasNormals) gi.setNormalIndices(normals);
+ if (hasTextures) {
+ for (int i = 0; i < texSetCount; i++) {
+ gi.setTextureCoordinateIndices(i, textures[i]);
+ }
+ }
+ if (hasColors) gi.setColorIndices(colors);
+ if (colorStrips) {
+ gi.setColors(stripColors);
+ colors = gi.getListIndices(stripColors);
+ gi.setColorIndices(colors);
+ }
+ }
+ }
+
+ /**
+ * Stores the infomration about a vertex
+ */
+ class Vertex {
+
+ int index;
+ int normal = EMPTY;
+ int numTexSets = 0;
+ int[] texture = null;
+ int color = EMPTY;
+
+ Vertex(int vertIndex) {
+ this(vertIndex, EMPTY, 0, null, EMPTY);
+ }
+
+ Vertex(int vertIndex, int vertNormal,
+ int vertNumTexSets, int[] vertTexture, int vertColor) {
+ index = vertIndex;
+ normal = vertNormal;
+ numTexSets = vertNumTexSets;
+ if (numTexSets > 0) {
+ texture = new int[numTexSets];
+ System.arraycopy(vertTexture, 0, texture, 0, numTexSets);
+ }
+ color = vertColor;
+ }
+
+ boolean equals(Vertex v) {
+ for (int i = 0; i < numTexSets; i++) {
+ if (texture[i] != v.texture[i]) {
+ return false;
+ }
+ }
+ return ((v.index == index) &&
+ (v.normal == normal) &&
+ (v.color == color));
+ }
+
+ // will this yield same results as c code ???
+ boolean lessThan(Vertex v) {
+ if (index < v.index) return true;
+ if (index > v.index) return false;
+ if (normal < v.normal) return true;
+ if (normal > v.normal) return false;
+ for (int i = 0; i < numTexSets; i++) {
+ if (texture[i] < v.texture[i]) return true;
+ if (texture[i] > v.texture[i]) return false;
+ }
+ if (color < v.color) return true;
+ if (color > v.color) return false;
+ return false;
+ }
+ }
+
+ /**
+ * Stores the information about an edge of a triangle
+ */
+ class Edge {
+
+ Vertex v1, v2;
+ int face;
+
+ Edge(Vertex vertex1, Vertex vertex2, int faceIndex) {
+ face = faceIndex;
+
+ // this could be causing wrapping problem
+ if (vertex1.lessThan(vertex2)) {
+ v1 = vertex1;
+ v2 = vertex2;
+ } else {
+ v1 = vertex2;
+ v2 = vertex1;
+ }
+ }
+
+ /**
+ * Determine whether the edges have the same vertices
+ */
+ boolean equals(Edge edge) {
+ return ((v1.equals(edge.v1)) && (v2.equals(edge.v2)));
+
+ }
+
+ /**
+ * Used to sort the edges. If this is less than the edge parameter,
+ * return true. First check if vertex1 is less than vertex1 of the
+ * edge provided. If so, return true. If the first vertices are equal
+ * then check vertex2.
+ */
+ boolean lessThan(Edge edge) {
+ if (v1.lessThan(edge.v1)) return true;
+ else if (v1.equals(edge.v1)) return (v2.lessThan(edge.v2));
+ else return false;
+ }
+ }
+
+ /**
+ * Stores the information about the face of a triangle
+ */
+ class Face {
+ int key;
+ int numNhbrs = 0;
+ Vertex[] verts = null;
+ // edges are kept in order s.t. the ith edge is the opposite
+ // edge of the ith vertex
+ Edge[] edges = null;
+
+ /**
+ * Creates a new Face with the three given vertices
+ */
+ Face(int index, Vertex v1, Vertex v2, Vertex v3) {
+ key = index;
+
+ verts = new Vertex[3];
+ verts[0] = v1;
+ verts[1] = v2;
+ verts[2] = v3;
+
+ edges = new Edge[3];
+ edges[0] = null;
+ edges[1] = null;
+ edges[2] = null;
+ numNhbrs = 3;
+ }
+
+ /**
+ * returns the index of the face that neighbors the edge supplied
+ * by the parameter
+ */
+ int getNeighbor(int edge) {
+ return edges[edge].face;
+ }
+
+ /**
+ * returns the index of the edge that is shared by the triangle
+ * specified by the key parameter
+ */
+ int findSharedEdge(int key) {
+ if (edges[0].face == key) return 0;
+ else if (edges[1].face == key) return 1;
+ else if (edges[2].face == key) return 2;
+ else return -1; /* error */
+ }
+
+ int getEdgeIndex(Edge edge) {
+ if (edges[0].equals(edge)) return 0;
+ else if (edges[1].equals(edge)) return 1;
+ else return 2;
+ }
+
+ void counterEdgeDel(Edge edge) {
+ if (DEBUG) {
+ System.out.println("counterEdgeDel");
+ }
+ if ((edges[0]).equals(edge)) {
+ edges[0].face = EMPTY;
+ numNhbrs--;
+ }
+ else if ((edges[1]).equals(edge)) {
+ edges[1].face = EMPTY;
+ numNhbrs--;
+ }
+ else if ((edges[2]).equals(edge)) {
+ edges[2].face = EMPTY;
+ numNhbrs--;
+ }
+ else {
+ if (DEBUG) {
+ System.out.println("error in counterEdgeDel");
+ }
+ }
+ }
+
+ void printAdjacency() {
+ System.out.println("Face " + key + ": ");
+ System.out.println("\t numNhbrs = " + numNhbrs);
+ System.out.println("\t edge 0: Face " + edges[0].face);
+ System.out.println("\t edge 1: Face " + edges[1].face);
+ System.out.println("\t edge 2: Face " + edges[2].face);
+ }
+
+ void printVertices() {
+ System.out.println("Face " + key + ": (" + verts[0].index + ", " +
+ verts[1].index + ", " + verts[2].index + ")");
+ }
+ }
+
+ /**
+ * stores the information for a face node
+ */
+ class Node {
+ Face face; // the data: the face
+ Node parent; // the parent node
+ Node left; // the left child
+ Node right; // the right child
+ int depth; // the topological distance of the node from the root
+ int numChildren; // the number of children
+ int attrib; // characteristic of the node eg. color
+
+ // the attributes - 3 states for the Node
+ static final int WHITE = 0; // not being accessed yet
+ static final int GREY = 1; // being accessed but not done yet
+ static final int BLACK = 2; // done
+
+ Node(Face f) {
+ face = f;
+ }
+
+ /**
+ * inserts this node below the parent supplied.
+ */
+ void insert(Node p) {
+ parent = p;
+ depth = p.depth + 1;
+ attrib = GREY;
+
+ if (parent.left == null) parent.left = this;
+ else parent.right = this;
+ (parent.numChildren)++;
+ }
+
+ /**
+ * remove this node from its parent
+ */
+ void remove() {
+ if (parent != null) {
+ if (parent.left == this) {
+ parent.left = parent.right;
+ parent.right = null;
+ }
+ else {
+ parent.right = null;
+ }
+ (parent.numChildren)--;
+ }
+ }
+
+
+ /**
+ * sets the depth to 0 and the attrib to GREY
+ */
+ void setRoot() {
+ depth = 0;
+ attrib = GREY;
+ }
+
+ /**
+ * returns true if the attrib is WHITE
+ */
+ boolean notAccessed() {
+ return (attrib == WHITE);
+ }
+
+ /**
+ * sets the color to BLACK
+ */
+ void processed() {
+ attrib = BLACK;
+ }
+
+ /**
+ * a node is the root if it doesn't have a parent
+ */
+ boolean isRoot() {
+ return (parent == null);
+ }
+
+ /**
+ * prints the information in this Node
+ */
+ void print() {
+ System.out.println(this);
+ System.out.println("Node depth: " + depth);
+ face.printVertices();
+ System.out.print("parent: ");
+ if (parent != null) parent.face.printVertices();
+ else System.out.println("null");
+ System.out.print("left: ");
+ if (left != null) left.face.printVertices();
+ else System.out.println("null");
+ System.out.print("right: ");
+ if (right != null) right.face.printVertices();
+ else System.out.println("null");
+ System.out.println("attrib: " + attrib);
+ System.out.println("");
+ }
+ }
+
+ /**
+ * sorts the Nodes by depth
+ */
+ class SortedList {
+
+ ArrayList list;
+
+ /**
+ * create a new SortedList
+ */
+ SortedList() {
+ list = new ArrayList();
+ }
+
+ /**
+ * insert into the list sorted by depth. start looking at start
+ * to save some time. Returns the index of the next item of the
+ * inserted element
+ */
+ int sortedInsert(Node data, int start) {
+ // adjust start to where insert sorted
+ while ((start < list.size()) &&
+ (((Node)list.get(start)).depth <= data.depth)) {
+ start++;
+ }
+
+ // insert at start index
+ list.add(start, data);
+
+ // return start+1 -- the index of the next element
+ return (start+1);
+ }
+
+ /**
+ * remove and return 1st element
+ */
+ Node pop() {
+ if (!list.isEmpty()) return (Node)list.remove(0);
+ else return null;
+ }
+ }
+
+ class Istream {
+
+ // fan encoding
+ boolean fan = false;
+ // length of the strip
+ int length = 0;
+ // array that specifies triangle strip
+ Vertex[] istream;
+ // indices of the head and tail vertices
+ int head, tail;
+
+ /**
+ * creates a new Istream to store the triangle strip
+ */
+ Istream(Vertex[] list, int size, boolean isFan) {
+ if (size == 0) throw new RuntimeException("size is 0");
+ fan = isFan;
+ length = size;
+ istream = new Vertex[length];
+ int i;
+ System.arraycopy(list, 0, istream, 0, length);
+ }
+
+ /**
+ * adds a new vertex to the end of the stream
+ * makes the int array bigger, if necessary
+ */
+ void append(Vertex vertex) {
+ growArray();
+ // add in new vertex
+ istream[length] = vertex;
+ length++;
+ }
+
+ /**
+ * turns the encoding (..., -3, -2, -1) into (.... -3, -2, -3, -1)
+ * so that zero-area triangle (-3, -2. -3) is added
+ */
+ void swapEnd() {
+ growArray();
+ istream[length] = istream[length-1];
+ istream[length-1] = istream[length-3];
+ length++;
+ }
+
+ /**
+ * makes the array bigger, if necessary
+ */
+ void growArray() {
+ if (length >= istream.length) {
+ Vertex[] old = istream;
+ // for now add enough space to add three more vertices
+ // may change this later
+ istream = new Vertex[length + 3];
+ System.arraycopy(old, 0, istream, 0, length);
+ }
+ }
+
+ /**
+ * inverts the istream
+ */
+ void invert() {
+ Vertex[] tmp = new Vertex[istream.length];
+ // reverse the stream
+ for (int i = 0; i < length; i++) {
+ tmp[i] = istream[length - i - 1];
+ }
+ // copy it back
+ System.arraycopy(tmp, 0, istream, 0, istream.length);
+ tmp = null;
+ // swap the head and the tail
+ int swap = head;
+ head = tail;
+ tail = swap;
+ }
+
+ /**
+ * concats two streams into one big stream
+ */
+ void addStream(Istream strm) {
+ // System.out.println("addStream");
+ int strmLen = strm.length;
+ int size = strmLen + length - 2;
+
+ // make the istream bigger
+ if (size >= istream.length) {
+ Vertex[] old = istream;
+ istream = new Vertex[size];
+ System.arraycopy(old, 0, istream, 0, length);
+ }
+
+ // add the strm to istream
+ System.arraycopy(strm.istream, 2, istream, length, strmLen-2);
+
+ tail = strm.tail;
+ length = size;
+ strm.length = 0;
+ strm.istream = null;
+ }
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/StripifierStats.java b/src/classes/share/com/sun/j3d/utils/geometry/StripifierStats.java
new file mode 100644
index 0000000..423ee18
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/StripifierStats.java
@@ -0,0 +1,345 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+import java.util.ArrayList;
+
+/**
+ * This class collects statistics on the Stripifier. The statistics
+ * are cumulative over all calls to stripify() until clearData() is called.
+ *
+ * @since Java 3D 1.2.1
+ */
+
+public class StripifierStats {
+
+ int numStrips = 0;
+ int numVerts = 0;
+ int minStripLen = 10000;
+ int maxStripLen = 0;
+ int totalTris = 0;
+ int numFaces = 0;
+ long time = 0;
+ int[] counts = new int[14];
+
+ boolean noData = true;
+
+ /**
+ * Returns the number of triangles in the original, un-stripified data.
+ * @since Java 3D 1.2.1
+ */
+ public int getNumOrigTris() {
+ return numFaces;
+ }
+
+ /**
+ * Returns the number of vertices in the original, un-stripified data
+ * @since Java 3D 1.2.1
+ */
+ public int getNumOrigVerts() {
+ return (numFaces * 3);
+ }
+
+ /**
+ * Returns the number of strips created by the stripifier.
+ * @since Java 3D 1.2.1
+ */
+ public int getNumStrips() {
+ return numStrips;
+ }
+
+ /**
+ * Returns the number of vertices in the stripified data.
+ * @since Java 3D 1.2.1
+ */
+ public int getNumVerts() {
+ return numVerts;
+ }
+
+ /**
+ * Returns the number of triangles in the stripified data.
+ * @since Java 3D 1.2.1
+ */
+ public int getTotalTris() {
+ return totalTris;
+ }
+
+ /**
+ * Returns the length in triangles of the shortest strip
+ * created by the stripifier.
+ * @since Java 3D 1.2.1
+ */
+ public int getMinStripLength() {
+ return minStripLen;
+ }
+
+ /**
+ * Returns the length in triangles of the longest strip
+ * created by the stripifier.
+ * @since Java 3D 1.2.1
+ */
+ public int getMaxStripLength() {
+ return maxStripLen;
+ }
+
+ /**
+ * Return the average length of the strips created by the stripifier
+ * @since Java 3D 1.2.1
+ */
+ public double getAvgStripLength() {
+ return ((double)totalTris/(double)numStrips);
+ }
+
+ /**
+ * Returns the average number of vertices per triangle in the stripified
+ * data
+ * @since Java 3D 1.2.1
+ */
+ public double getAvgNumVertsPerTri() {
+ return ((double)numVerts/(double)totalTris);
+ }
+
+ /**
+ * Returns the total time spent in the stripify() method
+ * @since Java 3D 1.2.1
+ */
+ public long getTotalTime() {
+ return time;
+ }
+
+ /**
+ * Returns an array of length 14 that contains the number of strips of
+ * a given length created by the stripifier. Spots 0-8 of the array
+ * represent lengths 1-9, 9 is lengths 10-19, 10 is lengths 20-49,
+ * 11 is lengths 50-99, 12 is lengths 100-999 and 13 is lengths 1000
+ * or more.
+ * @since Java 3D 1.2.1
+ */
+ public int[] getStripLengthCounts() {
+ return counts;
+ }
+
+ /**
+ * Returns a formated String that can be used to print out
+ * the Stripifier stats.
+ * @since Java 3D 1.2.1
+ */
+
+ public String toString() {
+ StringBuffer str = new StringBuffer(
+ "num orig tris: " + numFaces + "\n" +
+ "num orig vertices: " + (numFaces*3) + "\n" +
+ "number of strips: " + numStrips + "\n" +
+ "number of vertices: " + numVerts + "\n" +
+ "total tris: " + totalTris + "\n" +
+ "min strip length: " + minStripLen + "\n" +
+ "max strip length: " + maxStripLen + "\n" +
+ "avg strip length: " + ((double)totalTris/
+ (double)numStrips) + "\n" +
+ "avg num verts/tri: " + ((double)numVerts/
+ (double)totalTris) + "\n" +
+ "total time: " + time + "\n" +
+ "strip length distribution:\n");
+ for (int i = 0; i < 9; i++){
+ str.append(" " + (i+1) + "=" + counts[i]);
+ }
+ str.append(" 10-19=" + counts[9]);
+ str.append(" 20-49=" + counts[10]);
+ str.append(" 50-99=" + counts[11]);
+ str.append(" 100-999=" + counts[12]);
+ str.append(" 1000 or more=" + counts[13] + "\n");
+
+ return str.toString();
+ }
+
+ /**
+ * Clears the statistical data
+ */
+ public void clearData() {
+ noData = true;
+
+ numStrips = 0;
+ numVerts = 0;
+ minStripLen = 10000;
+ maxStripLen = 0;
+ totalTris = 0;
+ numFaces = 0;
+ time = 0;
+ counts = new int[14];
+ }
+
+ void updateInfo(long ntime, ArrayList strips,
+ int nNumFaces) {
+ noData = false;
+
+ time += ntime;
+ numStrips += strips.size();
+ int nv = 0;
+ int mnsl = 10000;
+ int mxsl = 0;
+ int tt = 0;
+ for (int i = 0; i < strips.size(); i++) {
+ Stripifier.Istream strm = (Stripifier.Istream)strips.get(i);
+ int len = strm.length;
+ int trilen = (len-2);
+ nv += len;
+ if (trilen < mnsl) mnsl = trilen;
+ if (trilen > mxsl) mxsl = trilen;
+ tt += trilen;
+
+ // add to counts
+ // how many strips are length 1-9
+ if (trilen <= 9) counts[trilen-1] += 1;
+ // how many strips are length 10-19
+ else if (trilen < 20) counts[9] += 1;
+ // how many strips are length 20-49
+ else if (trilen < 50) counts[10] += 1;
+ // how many strips are length 50-99
+ else if (trilen < 100) counts[11] += 1;
+ // how many strips are length 100-1000
+ else if (trilen < 1000) counts[12] += 1;
+ // how many strips are length > 1000
+ else counts[13] += 1;
+ }
+ numVerts += nv;
+ if (mnsl < minStripLen) minStripLen = mnsl;
+ if (mxsl > maxStripLen) maxStripLen = mxsl;
+ totalTris += tt;
+ numFaces += nNumFaces;
+ }
+
+ void updateInfo(long ntime, int scLen, int sc[],
+ int nNumFaces) {
+
+ noData = false;
+
+ time += ntime;
+ numStrips += scLen;
+ int nv = 0;
+ int mnsl = 10000;
+ int mxsl = 0;
+ int tt = 0;
+ for (int i = 0; i < scLen; i++) {
+ int len = sc[i];
+ int trilen = (len-2);
+ numVerts += len;
+ if (trilen < mnsl) mnsl = trilen;
+ if (trilen > mxsl) mxsl = trilen;
+ totalTris += trilen;
+
+ // add to counts
+ // how many strips are length 1-9
+ if (trilen <= 9) counts[trilen-1] += 1;
+ // how many strips are length 10-19
+ else if (trilen < 20) counts[9] += 1;
+ // how many strips are length 20-49
+ else if (trilen < 50) counts[10] += 1;
+ // how many strips are length 50-99
+ else if (trilen < 100) counts[11] += 1;
+ // how many strips are length 100-1000
+ else if (trilen < 1000) counts[12] += 1;
+ // how many strips are length > 1000
+ else counts[13] += 1;
+ }
+ numVerts += nv;
+ if (mnsl < minStripLen) minStripLen = mnsl;
+ if (mxsl > maxStripLen) maxStripLen = mxsl;
+ totalTris += tt;
+ numFaces += nNumFaces;
+ }
+
+ // void printInfo() {
+ // System.out.println("num orig tris: " + numFaces);
+ // System.out.println("num orig vertices: " + (numFaces*3));
+ // System.out.println("number of strips: " + numStrips);
+ // System.out.println("number of vertices: " + numVerts);
+ // System.out.println("total tris: " + totalTris);
+ // System.out.println("min strip length: " + minStripLen);
+ // System.out.println("max strip length: " + maxStripLen);
+ // System.out.println("avg strip length: " + ((double)totalTris/
+ // (double)numStrips));
+ // System.out.println("avg num verts/tri: " + ((double)numVerts/
+ // (double)totalTris));
+ // System.out.println("total time: " + time);
+ // System.out.println("strip length distribution:");
+ // for (int i = 0; i < 9; i++){
+ // System.out.print(" " + (i+1) + "=" + counts[i]);
+ // }
+ // System.out.print(" 10-19=" + counts[9]);
+ // System.out.print(" 20-49=" + counts[10]);
+ // System.out.print(" 50-99=" + counts[11]);
+ // System.out.print(" 100-999=" + counts[12]);
+ // System.out.println(" 1000 or more=" + counts[13]);
+
+ // // reset info after printing data
+ // numStrips = 0;
+ // numVerts = 0;
+ // minStripLen = 10000;
+ // maxStripLen = 0;
+ // totalTris = 0;
+ // numFaces = 0;
+ // time = 0;
+ // counts = new int[14];
+ // }
+
+ StripifierStats() {
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Text2D.java b/src/classes/share/com/sun/j3d/utils/geometry/Text2D.java
new file mode 100644
index 0000000..291c757
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Text2D.java
@@ -0,0 +1,382 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.geometry;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.WritableRaster;
+import java.awt.image.DataBufferInt;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Toolkit;
+import java.util.Hashtable;
+
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+/**
+ * A Text2D object is a representation of a string as a texture mapped
+ * rectangle. The texture for the rectangle shows the string as rendered in
+ * the specified color with a transparent background. The appearance of the
+ * characters is specified using the font indicated by the font name, size
+ * and style (see java.awt.Font). The approximate height of the rendered
+ * string will be the font size times the rectangle scale factor, which has a
+ * default value of 1/256. For example, a 12 point font will produce
+ * characters that are about 12/256 = 0.047 meters tall. The lower left
+ * corner of the rectangle is located at (0,0,0) with the height
+ * extending along the positive y-axis and the width extending along the
+ * positive x-axis.
+ */
+public class Text2D extends Shape3D {
+
+ // This table caches FontMetrics objects to avoid the huge cost
+ // of re-retrieving metrics for a font we've already seen.
+ private static Hashtable metricsTable = new Hashtable();
+ float rectangleScaleFactor = 1f/256f;
+
+ Color3f color = new Color3f();
+ String fontName;
+ int fontSize, fontStyle;
+ String text;
+
+
+ /**
+ * Creates a Shape3D object which holds a
+ * rectangle that is texture-mapped with an image that has
+ * the specified text written with the specified font
+ * parameters.
+ *
+ * @param text The string to be written into the texture map.
+ * @param color The color of the text string.
+ * @param fontName The name of the Java font to be used for
+ * the text string.
+ * @param fontSize The size of the Java font to be used.
+ * @param fontStyle The style of the Java font to be used.
+ */
+ public Text2D(String text, Color3f color, String fontName,
+ int fontSize, int fontStyle) {
+
+ this.color.set(color);
+ this.fontName = fontName;
+ this.fontSize = fontSize;
+ this.fontStyle = fontStyle;
+ this.text = text;
+
+ updateText2D(text, color, fontName, fontSize, fontStyle);
+ }
+
+ /*
+ * Changes text of this Text2D to 'text'. All other
+ * parameters (color, fontName, fontSize, fontStyle
+ * remain the same.
+ * @param text The string to be set.
+ */
+ public void setString(String text){
+ this.text = text;
+
+ Texture tex = getAppearance().getTexture();
+ int width = tex.getWidth();
+ int height = tex.getHeight();
+
+ ImageComponent imageComponent = setupImage(text, color, fontName,
+ fontSize, fontStyle);
+ if ((imageComponent.getWidth() == width) &&
+ (imageComponent.getHeight() == height)) {
+ tex.setImage(0, imageComponent);
+ } else {
+ Texture2D newTex = setupTexture(imageComponent);
+ // Copy texture attributes except those related to
+ // mipmap since Texture only set base imageComponent.
+
+ newTex.setBoundaryModeS(tex.getBoundaryModeS());
+ newTex.setBoundaryModeT(tex.getBoundaryModeT());
+ newTex.setMinFilter(tex.getMinFilter());
+ newTex.setMagFilter(tex.getMagFilter());
+ newTex.setEnable(tex.getEnable());
+ newTex.setAnisotropicFilterMode(tex.getAnisotropicFilterMode());
+ newTex.setAnisotropicFilterDegree(tex.getAnisotropicFilterDegree());
+ int pcount = tex.getFilter4FuncPointsCount();
+ if (pcount > 0) {
+ float weights[] = new float[pcount];
+ tex.getFilter4Func(weights);
+ newTex.setFilter4Func(weights);
+ }
+ Color4f c = new Color4f();
+ tex.getBoundaryColor(c);
+ newTex.setBoundaryColor(c);
+ newTex.setUserData(tex.getUserData());
+ getAppearance().setTexture(newTex);
+ }
+ }
+
+ private void updateText2D(String text, Color3f color, String fontName,
+ int fontSize, int fontStyle) {
+ ImageComponent imageComponent = setupImage(text, color, fontName,
+ fontSize, fontStyle);
+
+ Texture2D t2d = setupTexture(imageComponent);
+
+ QuadArray rect = setupGeometry(imageComponent.getWidth(),
+ imageComponent.getHeight());
+ setGeometry(rect);
+
+ Appearance appearance = setupAppearance(t2d);
+ setAppearance(appearance);
+ }
+
+
+ /**
+ * Sets the scale factor used in converting the image width/height
+ * to width/height values in 3D.
+ *
+ * @param newScaleFactor The new scale factor.
+ */
+ public void setRectangleScaleFactor(float newScaleFactor) {
+ rectangleScaleFactor = newScaleFactor;
+ updateText2D(text, color, fontName, fontSize, fontStyle);
+ }
+
+ /**
+ * Gets the current scale factor being used in converting the image
+ * width/height to width/height values in 3D.
+ *
+ * @return The current scale factor.
+ */
+ public float getRectangleScaleFactor() {
+ return rectangleScaleFactor;
+ }
+
+ /**
+ * Create the ImageComponent and Texture object.
+ */
+ private Texture2D setupTexture(ImageComponent imageComponent) {
+ Texture2D t2d = new Texture2D(Texture2D.BASE_LEVEL,
+ Texture.RGBA,
+ imageComponent.getWidth(),
+ imageComponent.getHeight());
+ t2d.setMinFilter(t2d.BASE_LEVEL_LINEAR);
+ t2d.setMagFilter(t2d.BASE_LEVEL_LINEAR);
+ t2d.setImage(0, imageComponent);
+ t2d.setEnable(true);
+ t2d.setCapability(Texture.ALLOW_IMAGE_WRITE);
+ t2d.setCapability(Texture.ALLOW_SIZE_READ);
+ t2d.setCapability(Texture.ALLOW_ENABLE_READ);
+ t2d.setCapability(Texture.ALLOW_BOUNDARY_MODE_READ);
+ t2d.setCapability(Texture.ALLOW_FILTER_READ);
+ t2d.setCapability(Texture.ALLOW_BOUNDARY_COLOR_READ);
+ t2d.setCapability(Texture.ALLOW_ANISOTROPIC_FILTER_READ);
+ t2d.setCapability(Texture.ALLOW_FILTER4_READ);
+ return t2d;
+ }
+
+ /**
+ * Creates a ImageComponent2D of the correct dimensions for the
+ * given font attributes. Draw the given text into the image in
+ * the given color. The background of the image is transparent
+ * (alpha = 0).
+ */
+ private ImageComponent setupImage(String text, Color3f color,
+ String fontName,
+ int fontSize, int fontStyle) {
+ Toolkit toolkit = Toolkit.getDefaultToolkit();
+ Font font = new java.awt.Font(fontName, fontStyle, fontSize);
+
+ FontMetrics metrics;
+ if ((metrics = (FontMetrics)metricsTable.get(font)) == null) {
+ metrics = toolkit.getFontMetrics(font);
+ metricsTable.put(font, metrics);
+ }
+ int width = metrics.stringWidth(text);
+ int descent = metrics.getMaxDescent();
+ int ascent = metrics.getMaxAscent();
+ int leading = metrics.getLeading();
+ int height = descent + ascent;
+
+ // Need to make width/height powers of 2 because of Java3d texture
+ // size restrictions
+ int pow = 1;
+ for (int i = 1; i < 32; ++i) {
+ pow *= 2;
+ if (width <= pow)
+ break;
+ }
+ width = Math.max (width, pow);
+ pow = 1;
+ for (int i = 1; i < 32; ++i) {
+ pow *= 2;
+ if (height <= pow)
+ break;
+ }
+ height = Math.max (height, pow);
+
+ // For now, jdk 1.2 only handles ARGB format, not the RGBA we want
+ BufferedImage bImage = new BufferedImage(width, height,
+ BufferedImage.TYPE_INT_ARGB);
+ Graphics offscreenGraphics = bImage.createGraphics();
+
+ // First, erase the background to the text panel - set alpha to 0
+ Color myFill = new Color(0f, 0f, 0f, 0f);
+ offscreenGraphics.setColor(myFill);
+ offscreenGraphics.fillRect(0, 0, width, height);
+
+ // Next, set desired text properties (font, color) and draw String
+ offscreenGraphics.setFont(font);
+ Color myTextColor = new Color(color.x, color.y, color.z, 1f);
+ offscreenGraphics.setColor(myTextColor);
+ offscreenGraphics.drawString(text, 0, height - descent);
+
+ ImageComponent imageComponent =
+ new ImageComponent2D(ImageComponent.FORMAT_RGBA,
+ bImage);
+
+ imageComponent.setCapability(ImageComponent.ALLOW_SIZE_READ);
+
+ return imageComponent;
+ }
+
+ /**
+ * Creates a rectangle of the given width and height and sets up
+ * texture coordinates to map the text image onto the whole surface
+ * of the rectangle (the rectangle is the same size as the text image)
+ */
+ private QuadArray setupGeometry(int width, int height) {
+ float zPosition = 0f;
+ float rectWidth = (float)width * rectangleScaleFactor;
+ float rectHeight = (float)height * rectangleScaleFactor;
+ float[] verts1 = {
+ rectWidth, 0f, zPosition,
+ rectWidth, rectHeight, zPosition,
+ 0f, rectHeight, zPosition,
+ 0f, 0f, zPosition
+ };
+ float[] texCoords = {
+ 0f, -1f,
+ 0f, 0f,
+ (-1f), 0f,
+ (-1f), -1f
+ };
+
+ QuadArray rect = new QuadArray(4, QuadArray.COORDINATES |
+ QuadArray.TEXTURE_COORDINATE_2);
+ rect.setCoordinates(0, verts1);
+ rect.setTextureCoordinates(0, 0, texCoords);
+
+ return rect;
+ }
+
+ /**
+ * Creates Appearance for this Shape3D. This sets transparency
+ * for the object (we want the text to be "floating" in space,
+ * so only the text itself should be non-transparent. Also, the
+ * appearance disables lighting for the object; the text will
+ * simply be colored, not lit.
+ */
+ private Appearance setupAppearance(Texture2D t2d) {
+ TransparencyAttributes transp = new TransparencyAttributes();
+ transp.setTransparencyMode(TransparencyAttributes.BLENDED);
+ transp.setTransparency(0f);
+ Appearance appearance = new Appearance();
+ appearance.setTransparencyAttributes(transp);
+ appearance.setTexture(t2d);
+
+ Material m = new Material();
+ m.setLightingEnable(false);
+ appearance.setMaterial(m);
+
+ return appearance;
+ }
+
+ /**
+ * Returns the text string
+ *
+ * @since Java 3D 1.2.1
+ */
+ public String getString() {
+ return text;
+ }
+
+ /**
+ * Returns the color of the text
+ *
+ * @since Java 3D 1.2.1
+ */
+ public Color3f getColor() {
+ return color;
+ }
+
+ /**
+ * Returns the font
+ *
+ * @since Java 3D 1.2.1
+ */
+ public String getFontName() {
+ return fontName;
+ }
+
+ /**
+ * Returns the font size
+ *
+ * @since Java 3D 1.2.1
+ */
+ public int getFontSize() {
+ return fontSize;
+ }
+
+ /**
+ * Returns the font style
+ *
+ * @since Java 3D 1.2.1
+ */
+ public int getFontStyle() {
+ return fontStyle;
+ }
+
+}
+
+
+
+
+
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Triangle.java b/src/classes/share/com/sun/j3d/utils/geometry/Triangle.java
new file mode 100644
index 0000000..b543f6d
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Triangle.java
@@ -0,0 +1,69 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+class Triangle extends Object {
+ int v1, v2, v3; // This store the index into the list array.
+ // Not the index into vertex pool yet!
+
+ Triangle(int a, int b, int c) {
+ v1=a; v2=b; v3=c;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/geometry/Triangulator.java b/src/classes/share/com/sun/j3d/utils/geometry/Triangulator.java
new file mode 100644
index 0000000..c8c272b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/geometry/Triangulator.java
@@ -0,0 +1,1049 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+// ----------------------------------------------------------------------
+//
+// The reference to Fast Industrial Strength Triangulation (FIST) code
+// in this release by Sun Microsystems is related to Sun's rewrite of
+// an early version of FIST. FIST was originally created by Martin
+// Held and Joseph Mitchell at Stony Brook University and is
+// incorporated by Sun under an agreement with The Research Foundation
+// of SUNY (RFSUNY). The current version of FIST is available for
+// commercial use under a license agreement with RFSUNY on behalf of
+// the authors and Stony Brook University. Please contact the Office
+// of Technology Licensing at Stony Brook, phone 631-632-9009, for
+// licensing information.
+//
+// ----------------------------------------------------------------------
+
+package com.sun.j3d.utils.geometry;
+
+import javax.vecmath.*;
+import java.util.*;
+import com.sun.j3d.utils.geometry.GeometryInfo;
+import com.sun.j3d.internal.J3dUtilsI18N;
+
+/**
+ * Triangulator is a utility for turning arbitrary polygons into triangles
+ * so they can be rendered by Java 3D.
+ * Polygons can be concave, nonplanar, and can contain holes.
+ * @see GeometryInfo
+ */
+public class Triangulator extends Object {
+
+ GeometryInfo gInfo = null;
+
+ int faces[] = null;
+ int loops[] = null;
+ int chains[] = null;
+ Point2f points[] = null;
+ Triangle triangles[] = null;
+ ListNode list[] = null;
+
+ Random randomGen = null;
+
+ int numPoints = 0;
+ int maxNumPoints = 0;
+ int numList = 0;
+ int maxNumList = 0;
+ int numLoops = 0;
+ int maxNumLoops = 0;
+ int numTriangles = 0;
+ int maxNumTriangles = 0;
+
+ int numFaces = 0;
+ int numTexSets = 0;
+ // int maxNumFaces = 0;
+
+ int firstNode = 0;
+
+ int numChains = 0;
+ int maxNumChains = 0;
+
+ // For Clean class.
+ Point2f[] pUnsorted = null;
+ int maxNumPUnsorted = 0;
+
+ // For NoHash class.
+ boolean noHashingEdges = false;
+ boolean noHashingPnts = false;
+ int loopMin, loopMax;
+ PntNode vtxList[] = null;
+ int numVtxList = 0;
+ int numReflex = 0;
+ int reflexVertices;
+
+ // For Bridge class.
+ Distance distances[] = null;
+ int maxNumDist = 0;
+ Left leftMost[] = null;
+ int maxNumLeftMost = 0;
+
+ // For Heap class.
+ HeapNode heap[] = null;
+ int numHeap = 0;
+ int maxNumHeap = 0;
+ int numZero = 0;
+
+ // For Orientation class.
+ int maxNumPolyArea = 0;
+ double polyArea[] = null;
+
+ int stripCounts[] = null;
+ int vertexIndices[] = null;
+ Point3f vertices[] = null;
+ Object colors[] = null;
+ Vector3f normals[] = null;
+
+ boolean ccwLoop = true;
+
+ boolean earsRandom = true;
+ boolean earsSorted = true;
+
+ int identCntr; // Not sure what is this for. (Ask Martin)
+
+ // double epsilon = 1.0e-12;
+ double epsilon = 1.0e-12;
+
+ static final double ZERO = 1.0e-8;
+ static final int EARS_SEQUENCE = 0;
+ static final int EARS_RANDOM = 1;
+ static final int EARS_SORTED = 2;
+
+
+ static final int INC_LIST_BK = 100;
+ static final int INC_LOOP_BK = 20;
+ static final int INC_TRI_BK = 50;
+ static final int INC_POINT_BK = 100;
+ static final int INC_DIST_BK = 50;
+
+ private static final int DEBUG = 0;
+
+ /**
+ * Creates a new instance of the Triangulator.
+ * @deprecated This class is created automatically when needed in
+ * GeometryInfo and never needs to be used directly. Putting data
+ * into a GeometryInfo with primitive POLYGON_ARRAY automatically
+ * causes the triangulator to be created and used.
+ */
+ public Triangulator() {
+ earsRandom = false;
+ earsSorted = false;
+ }
+
+ /**
+ * Creates a new instance of a Triangulator.
+ * @deprecated This class is created automatically when needed in
+ * GeometryInfo and never needs to be used directly. Putting data
+ * into a GeometryInfo with primitive POLYGON_ARRAY automatically
+ * causes the triangulator to be created and used.
+ */
+ public Triangulator(int earOrder) {
+ switch(earOrder) {
+ case EARS_SEQUENCE:
+ earsRandom = false;
+ earsSorted = false;
+ break;
+ case EARS_RANDOM:
+ randomGen = new Random();
+ earsRandom = true;
+ earsSorted = false;
+ break;
+ case EARS_SORTED:
+ earsRandom = false;
+ earsSorted = true;
+ break;
+ default:
+ earsRandom = false;
+ earsSorted = false;
+ }
+
+ }
+
+ /**
+ * This routine converts the GeometryInfo object from primitive type
+ * POLYGON_ARRAY to primitive type TRIANGLE_ARRAY using polygon
+ * decomposition techniques.
+ * <p>
+ * <pre>
+ * Example of usage:
+ * Triangulator tr = new Triangulator();
+ * tr.triangulate(ginfo); // ginfo contains the geometry.
+ * shape.setGeometry(ginfo.getGeometryArray()); // shape is a Shape3D.
+ *<p></pre>
+ * @param gi Geometry to be triangulated
+ **/
+ public void triangulate(GeometryInfo gi) {
+ int i, j, k;
+ int sIndex = 0, index, currLoop, lastInd, ind;
+ boolean proceed;
+ boolean reset = false, troubles = false;
+
+ boolean done[] = new boolean[1];
+ boolean gotIt[] = new boolean[1];
+
+ if (gi.getPrimitive() != GeometryInfo.POLYGON_ARRAY){
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("Triangulator0"));
+ }
+
+ gi.indexify();
+
+ vertices = gi.getCoordinates();
+ if(vertices != null)
+ vertexIndices = gi.getCoordinateIndices();
+ else
+ vertexIndices = null;
+
+ colors = gi.getColors();
+ normals = gi.getNormals();
+ this.gInfo= gi;
+
+
+ stripCounts = gi.getStripCounts();
+
+ faces = gi.getContourCounts();
+ if(faces == null) {
+ if(stripCounts == null)
+ System.out.println("StripCounts is null! Don't know what to do.");
+
+ faces = new int[stripCounts.length];
+ for(i=0; i<stripCounts.length; i++)
+ faces[i] = 1;
+ }
+
+ numFaces = faces.length;
+ numTexSets = gInfo.getTexCoordSetCount();
+
+
+ // For debugging ...
+ /*
+ System.out.println("Faces (number " + faces.length + ") : ");
+ for(i=0; i<faces.length; i++) {
+ System.out.println(faces[i] + ", ");
+ }
+
+ System.out.println("StripCounts (number " + stripCounts.length + ") : ");
+ for(i=0; i<stripCounts.length; i++) {
+ System.out.println(stripCounts[i] + ", ");
+ }
+
+ System.out.println("Vertices (number " + vertices.length + ") : ");
+ for(i=0; i<vertices.length; i++) {
+ System.out.println(i + " x " + vertices[i].x + " y " + vertices[i].y +
+ " z " + vertices[i].z);
+ }
+
+
+ System.out.println("VertexIndices (number " + vertexIndices.length + ") : ");
+
+ for(i=0; i<vertexIndices.length; i++) {
+ System.out.print(vertexIndices[i] + ", ");
+ }
+ */
+
+ maxNumLoops = 0;
+ maxNumList = 0;
+ maxNumPoints = 0;
+ maxNumDist = 0;
+ maxNumLeftMost = 0;
+ maxNumPUnsorted = 0;
+
+ // Compute the length of loops and list.
+ for(i=0; i<faces.length; i++) {
+ maxNumLoops += faces[i];
+ for(j=0; j<faces[i]; j++, sIndex++) {
+ maxNumList += (stripCounts[sIndex]+1);
+ }
+ }
+
+ // Add some incase of bridges.
+ maxNumList += 20;
+
+ loops = new int[maxNumLoops];
+ list = new ListNode[maxNumList];
+ // maxNumPoints = vertices.length;
+ //points = new Point2f[maxNumPoints];
+
+
+ // Construct data for use in triangulation.
+ numVtxList = 0;
+ numReflex = 0;
+
+ numTriangles = 0;
+ numChains = 0;
+ numPoints = 0;
+ numLoops = 0;
+ numList = 0;
+ sIndex = 0;
+ index = 0;
+
+ for(i=0; i<faces.length; i++) {
+ for(j=0; j<faces[i]; j++, sIndex++) {
+
+ currLoop = makeLoopHeader();
+ lastInd = loops[currLoop];
+
+ for(k=0; k<stripCounts[sIndex]; k++) {
+ list[numList] = new ListNode(vertexIndices[index]);
+ ind = numList++;
+
+ insertAfter(lastInd, ind);
+ list[ind].setCommonIndex(index);
+
+ lastInd = ind;
+ index++;
+
+ } // index k.
+
+ deleteHook(currLoop);
+
+ } // index j.
+ } // index i.
+
+
+ // Done with constructing data. We can start to triangulate now.
+
+ maxNumTriangles = maxNumList/2;
+ triangles = new Triangle[maxNumTriangles];
+
+ // set the numerical precision threshold
+ setEpsilon(ZERO);
+
+ // process the polygonal faces of the polyhedron. for every face, we
+ // check whether it is a triangle or a quadrangle in order to weed out
+ // simple cases which do not require the full triangulation algorithm
+
+ /*
+ for(i=0; i<numLoops; i++)
+ System.out.println("loops[" + i + "] " + loops[i]);
+ printListData();
+ */
+
+ int i1 = 0;
+ int i2 = 0;
+ for( j = 0; j < numFaces; ++j) {
+ ccwLoop = true;
+ done[0] = false;
+ i2 = i1 + faces[j];
+
+ if (faces[j] > 1) {
+ proceed = true;
+ }
+ else if(Simple.simpleFace(this, loops[i1]))
+ proceed = false;
+ else
+ proceed = true;
+
+ if (proceed) {
+
+
+ // Do some preprocessing here.
+
+ // System.out.println("faces["+j+"] "+faces[j]);
+ for(int lpIndex = 0; lpIndex<faces[j]; lpIndex++)
+ preProcessList(i1 + lpIndex);
+
+ // project the polygonal face onto a plane
+ Project.projectFace(this, i1, i2);
+
+ // sort the points of this face in lexicographic order and discard
+ // duplicates
+
+ // System.out.println("Before cleaning ...");
+ // printListData();
+
+
+ int removed = Clean.cleanPolyhedralFace(this, i1, i2);
+
+ // For debugging.
+ /*
+ System.out.println("After cleaning ...");
+ printListData();
+
+ System.out.println("\n***** " + removed +
+ " duplicate points removed for face " +
+ i1 + " *****\n");
+ */
+
+ // determine the orientation of the polygon; the default
+ // orientation is CCW for the outer polygon
+ if (faces[j] == 1) {
+ // System.out.println("determineOrientation");
+ Orientation.determineOrientation(this, loops[i1]);
+ }
+ else {
+ // System.out.println("adjustOrientation");
+ Orientation.adjustOrientation(this, i1, i2);
+ }
+
+ // CE2 only
+ if (faces[j] > 1) {
+ NoHash.prepareNoHashEdges(this, i1, i2);
+ }
+ else {
+ noHashingEdges = false;
+ noHashingPnts = false;
+ }
+
+
+ // mark those vertices whose interior angle is convex
+ for (i = i1; i < i2; ++i) {
+ EarClip.classifyAngles(this, loops[i]);
+ }
+
+ /*
+ System.out.println("After classifyAngles ...");
+ printListData();
+ */
+
+
+ // link the holes with the outer boundary by means of "bridges"
+ if (faces[j] > 1) Bridge.constructBridges(this, i1, i2);
+
+ // put all ears into a circular linked list
+ resetPolyList(loops[i1]);
+ NoHash.prepareNoHashPnts(this, i1);
+ EarClip.classifyEars(this, loops[i1]);
+ done[0] = false;
+
+ /*
+ System.out.println("Before clipEar (List)...");
+ printListData();
+ System.out.println("Before clipEar (vtxList)...");
+ printVtxList();
+
+ int counter = 0;
+ */
+
+ // triangulate the polygon
+ while (!done[0]) {
+ if (!EarClip.clipEar(this, done)) {
+ /*
+ System.out.println(" (False case) clipEar (vtxList)...");
+ printListData();
+ printVtxList();
+ */
+
+ if (reset) {
+ // For debugging.
+
+ // System.out.println("***** no further ear to clip! ***** \n");
+ // System.out.println("***** not a simple polygon, isn't it? *****\n");
+
+
+ ind = getNode();
+ resetPolyList(ind);
+
+ loops[i1] = ind;
+ if (Desperate.desperate(this, ind, i1, done)) {
+ // System.out.println("***** let's hope for the best *****\n");
+ if (!Desperate.letsHope(this, ind)) {
+ /*
+ System.out.println("***** sorry, I can't do it! ***** \n");
+ System.out.println("***** ask a triangulation wizard, or ");
+ System.out.println("clean-up your polyhedron! ***** \n");
+ */
+ return;
+ }
+ }
+ else {
+ reset = false;
+ }
+ }
+ else {
+ // try again from scratch
+ troubles = true;
+ // System.out.println("\n***** re-classifying the ears! ***** \n");
+ ind = getNode();
+ resetPolyList(ind);
+
+ // System.out.println("Before classifyEars(" + ind + ")");
+ // printListData();
+
+ EarClip.classifyEars(this, ind);
+ reset = true;
+ }
+ }
+ else {
+ reset = false;
+ /*
+ System.out.println(" (True case) clipEar (vtxList)...");
+
+ printVtxList();
+ */
+
+ }
+
+ if (done[0]) {
+ // System.out.println("In done[0] is true");
+ ind = getNextChain(gotIt);
+ if (gotIt[0]) {
+ // at some point of the triangulation, we could not find
+ // any ear and the polygon was split into two parts. now
+ // we have to handle (one of) the remaining parts.
+ resetPolyList(ind);
+ loops[i1] = ind;
+ noHashingPnts = false;
+ NoHash.prepareNoHashPnts(this, i1);
+ EarClip.classifyEars(this, ind);
+ reset = false;
+ done[0] = false;
+ }
+ }
+ }
+ }
+
+ i1 = i2;
+
+ }
+
+ /*
+ if (troubles)
+ System.out.println("\n\nTriangulation completed!\n");
+ else
+ System.out.println("\n\nTriangulation successfully completed!\n");
+ */
+ // System.out.println("\n...writing the output data: ");
+
+ // Output triangles here.
+ writeTriangleToGeomInfo();
+
+ }
+
+ void printVtxList() {
+ int i;
+ System.out.println("numReflex " + numReflex + " reflexVertices " +
+ reflexVertices);
+ for(i= 0; i<numVtxList; i++)
+ System.out.println(i + " pnt " + vtxList[i].pnt +
+ ", next " + vtxList[i].next );
+
+ }
+
+ void printListData() {
+ for(int i=0; i<numList; i++)
+ System.out.println("list[" + i + "].index " + list[i].index +
+ ", prev " + list[i].prev +
+ ", next " + list[i].next +
+ ", convex " + list[i].convex +
+ ", vertexIndex " + list[i].vcntIndex);
+
+ }
+
+ void preProcessList(int i1) {
+ int tInd, tInd1, tInd2;
+
+ resetPolyList(loops[i1]);
+ tInd = loops[i1];
+ tInd1 = tInd;
+ tInd2 = list[tInd1].next;
+ while(tInd2 != tInd) {
+ if(list[tInd1].index == list[tInd2].index) {
+ if(tInd2 == loops[i1])
+ loops[i1] = list[tInd2].next;
+ deleteLinks(tInd2);
+ }
+ tInd1 = list[tInd1].next;
+ tInd2 = list[tInd1].next;
+ }
+
+ }
+
+ void writeTriangleToGeomInfo() {
+ int i, currIndex;
+
+ // There are 2 approaches to take here : (1) Output all triangles as
+ // a single face.(Easy) (2) Preserve the faces of the polyhedron and
+ // sets of triangles per face. ( Seems to be the preferred approach, but
+ // a check in GeometryInfo and the old GeomInfoConverter doesn't seems
+ // to support this. Will check with Dan and Paul. Will take the easy way first.
+
+ // For debugging ....
+ if(DEBUG == 1) {
+ System.out.println("List (number " + numList + ") : ");
+ for(i=0; i<numList; i++) {
+ System.out.println("index " + list[i].index + " prev " + list[i].prev +
+ " next " + list[i].next + " convex " + list[i].convex);
+ System.out.println(" vertexIndex " + list[i].vcntIndex + " colorIndex " +
+ list[i].vcntIndex + " normalIndex " + list[i].vcntIndex +
+ " textureIndex " + list[i].vcntIndex);
+ }
+
+ System.out.println("Points (number " + numPoints + ") : ");
+
+ for(i=0; i<numPoints; i++) {
+ System.out.println("x " + points[i].x + " y " + points[i].y);
+ }
+
+ System.out.println("Triangles (number " + numTriangles + ") : ");
+
+ for(i=0; i<numTriangles; i++) {
+ System.out.println("v1 " + triangles[i].v1 + " v2 " + triangles[i].v2 +
+ " v3 " + triangles[i].v3);
+ }
+ }
+
+ gInfo.setPrimitive(GeometryInfo.TRIANGLE_ARRAY);
+ gInfo.setContourCounts(null);
+ gInfo.forgetOldPrim();
+ gInfo.setStripCounts(null);
+
+ currIndex = 0;
+ int newVertexIndices[] = new int[numTriangles*3];
+ int index;
+ for(i=0; i<numTriangles; i++) {
+ index = list[triangles[i].v1].getCommonIndex();
+ newVertexIndices[currIndex++] = vertexIndices[index];
+ index = list[triangles[i].v2].getCommonIndex();
+ newVertexIndices[currIndex++] = vertexIndices[index];
+ index = list[triangles[i].v3].getCommonIndex();
+ newVertexIndices[currIndex++] = vertexIndices[index];
+ }
+ gInfo.setCoordinateIndices(newVertexIndices);
+
+ /*
+ for(i=0;i<newVertexIndices.length;) {
+ System.out.println("v1 " + newVertexIndices[i++] +
+ ", v2 " + newVertexIndices[i++] +
+ ", v3 " + newVertexIndices[i++]);
+ }
+
+ System.out.println("Pysical point:");
+ for(i=0;i<newVertexIndices.length;) {
+ System.out.println("v1 " + vertices[newVertexIndices[i++]] +
+ ", v2 " + vertices[newVertexIndices[i++]] +
+ ", v3 " + vertices[newVertexIndices[i++]]);
+ }
+ */
+
+ if(normals != null) {
+ int oldNormalIndices[] = gInfo.getNormalIndices();
+ int newNormalIndices[] = new int[numTriangles*3];
+ currIndex = 0;
+ for(i=0; i<numTriangles; i++) {
+ index = list[triangles[i].v1].getCommonIndex();
+ newNormalIndices[currIndex++] = oldNormalIndices[index];
+ index = list[triangles[i].v2].getCommonIndex();
+ newNormalIndices[currIndex++] = oldNormalIndices[index];
+ index = list[triangles[i].v3].getCommonIndex();
+ newNormalIndices[currIndex++] = oldNormalIndices[index];
+ }
+ gInfo.setNormalIndices(newNormalIndices);
+ }
+
+ if(colors != null) {
+ currIndex = 0;
+ int oldColorIndices[] = gInfo.getColorIndices();
+ int newColorIndices[] = new int[numTriangles*3];
+ for(i=0; i<numTriangles; i++) {
+ index = list[triangles[i].v1].getCommonIndex();
+ newColorIndices[currIndex++] = oldColorIndices[index];
+ index = list[triangles[i].v2].getCommonIndex();
+ newColorIndices[currIndex++] = oldColorIndices[index];
+ index = list[triangles[i].v3].getCommonIndex();
+ newColorIndices[currIndex++] = oldColorIndices[index];
+ }
+ gInfo.setColorIndices(newColorIndices);
+ }
+
+ for(int j = 0; j < numTexSets; j++) {
+ int newTextureIndices[] = new int[numTriangles*3];
+ int oldTextureIndices[] = gInfo.getTextureCoordinateIndices(j);
+ currIndex = 0;
+ for(i=0; i<numTriangles; i++) {
+ index = list[triangles[i].v1].getCommonIndex();
+ newTextureIndices[currIndex++] = oldTextureIndices[index];
+ index = list[triangles[i].v2].getCommonIndex();
+ newTextureIndices[currIndex++] = oldTextureIndices[index];
+ index = list[triangles[i].v3].getCommonIndex();
+ newTextureIndices[currIndex++] = oldTextureIndices[index];
+ }
+ gInfo.setTextureCoordinateIndices(j, newTextureIndices);
+ }
+ }
+
+
+ void setEpsilon(double eps) {
+ epsilon = eps;
+ }
+
+ // Methods of handling ListNode.
+
+ boolean inPolyList(int ind) {
+ return ((ind >= 0) && (ind < numList) && (numList <= maxNumList));
+ }
+
+
+
+ void updateIndex(int ind, int index) {
+ // assert(InPolyList(ind));
+ list[ind].index = index;
+ }
+
+ int getAngle(int ind) {
+ return list[ind].convex;
+ }
+
+ void setAngle(int ind, int convex) {
+ list[ind].convex = convex;
+ }
+
+
+ void resetPolyList(int ind) {
+ // assert(InPolyList(ind));
+ firstNode = ind;
+ }
+
+ int getNode() {
+ // assert(InPolyList(first_node));
+ return firstNode;
+ }
+
+ boolean inLoopList(int loop) {
+ return ((loop >= 0) && (loop < numLoops) && (numLoops <= maxNumLoops));
+ }
+
+
+ void deleteHook(int currLoop) {
+ int ind1, ind2;
+
+ if(inLoopList(currLoop)==false)
+ System.out.println("Triangulator:deleteHook : Loop access out of range.");
+
+ ind1 = loops[currLoop];
+ ind2 = list[ind1].next;
+ if((inPolyList(ind1))&&(inPolyList(ind2))) {
+
+ deleteLinks(ind1);
+ loops[currLoop] = ind2;
+
+ }
+ else
+ System.out.println("Triangulator:deleteHook : List access out of range.");
+ }
+
+ /**
+ * Deletes node ind from list (with destroying its data fields)
+ */
+ void deleteLinks(int ind) {
+
+ if((inPolyList(ind))&&(inPolyList(list[ind].prev))&&
+ (inPolyList(list[ind].next))) {
+
+ if (firstNode == ind)
+ firstNode = list[ind].next;
+
+ list[list[ind].next].prev = list[ind].prev;
+ list[list[ind].prev].next = list[ind].next;
+ list[ind].prev = list[ind].next = ind;
+
+ }
+ else
+ System.out.println("Triangulator:deleteLinks : Access out of range.");
+
+ }
+
+ void rotateLinks(int ind1, int ind2) {
+ int ind;
+ int ind0, ind3;
+
+ // assert(InPolyList(ind1));
+ // assert(InPolyList(ind2));
+ ind0 = list[ind1].next;
+ ind3 = list[ind2].next;
+ // assert(InPolyList(ind0));
+ // assert(InPolyList(ind3));
+
+ // Swap.
+ ind = list[ind1].next;
+ list[ind1].next = list[ind2].next;
+ list[ind2].next = ind;
+
+ list[ind0].prev = ind2;
+ list[ind3].prev = ind1;
+
+ }
+
+
+ void storeChain(int ind) {
+ if (numChains >= maxNumChains) {
+ // System.out.println("Triangulator:storeChain Expanding chain array ...");
+ maxNumChains += 20;
+ int old[] = chains;
+ chains = new int[maxNumChains];
+ if(old != null)
+ System.arraycopy(old, 0, chains, 0, old.length);
+ }
+ chains[numChains] = ind;
+ ++numChains;
+
+ }
+
+ int getNextChain(boolean[] done) {
+ if (numChains > 0) {
+ done[0] = true;
+ --numChains;
+ return chains[numChains];
+ }
+ else {
+ done[0] = false;
+ numChains = 0;
+ return 0;
+ }
+ }
+
+ void splitSplice(int ind1, int ind2, int ind3, int ind4) {
+ list[ind1].next = ind4;
+ list[ind4].prev = ind1;
+ list[ind2].prev = ind3;
+ list[ind3].next = ind2;
+
+ }
+
+ /**
+ * Allocates storage for a dummy list node; pointers are set to itself.
+ * @return pointer to node
+ */
+ int makeHook() {
+ int ind;
+
+ ind = numList;
+ if (numList >= maxNumList) {
+ maxNumList += INC_LIST_BK;
+ // System.out.println("Triangulator: Expanding list array ....");
+ ListNode old[] = list;
+ list = new ListNode[maxNumList];
+ System.arraycopy(old, 0, list, 0, old.length);
+ }
+
+ list[numList] = new ListNode(-1);
+ list[numList].prev = ind;
+ list[numList].next = ind;
+ list[numList].index = -1;
+ ++numList;
+
+ return ind;
+ }
+
+ int makeLoopHeader() {
+ int i;
+ int ind;
+
+ ind = makeHook();
+ if(numLoops >= maxNumLoops) {
+ maxNumLoops += INC_LOOP_BK;
+ // System.out.println("Triangulator: Expanding loops array ....");
+ int old[] = loops;
+ loops = new int[maxNumLoops];
+ System.arraycopy(old, 0, loops, 0, old.length);
+ }
+
+ loops[numLoops] = ind;
+ i = numLoops;
+ ++numLoops;
+
+ return i;
+ }
+
+
+ /**
+ * Allocates storage for a new list node, and stores the index of the point
+ * at this node. Pointers are set to -1.
+ * @return pointer to node
+ */
+ int makeNode(int index) {
+ int ind;
+
+ if (numList >= maxNumList) {
+ maxNumList += INC_LIST_BK;
+ //System.out.println("Triangulator: Expanding list array ....");
+ ListNode old[] = list;
+ list = new ListNode[maxNumList];
+ System.arraycopy(old, 0, list, 0, old.length);
+ }
+
+ list[numList] = new ListNode(index);
+
+ ind = numList;
+ list[numList].index = index;
+ list[numList].prev = -1;
+ list[numList].next = -1;
+ ++numList;
+
+ return ind;
+ }
+
+
+ /**
+ * Inserts node ind2 after node ind1.
+ */
+ void insertAfter(int ind1, int ind2) {
+ int ind3;
+
+ if((inPolyList(ind1))&&(inPolyList(ind2))) {
+
+ list[ind2].next = list[ind1].next;
+ list[ind2].prev = ind1;
+ list[ind1].next = ind2;
+ ind3 = list[ind2].next;
+
+ if(inPolyList(ind3))
+ list[ind3].prev = ind2;
+ else
+ System.out.println("Triangulator:deleteHook : List access out of range.");
+
+ return;
+ }
+ else
+ System.out.println("Triangulator:deleteHook : List access out of range.");
+
+ }
+
+ /**
+ * Returns pointer to the successor of ind1.
+ */
+ int fetchNextData(int ind1) {
+ return list[ind1].next;
+ }
+
+ /**
+ * obtains the data store at ind1
+ */
+ int fetchData(int ind1) {
+ return list[ind1].index;
+ }
+
+ /**
+ * returns pointer to the successor of ind1.
+ */
+ int fetchPrevData(int ind1) {
+ return list[ind1].prev;
+ }
+
+ /**
+ * swap the list pointers in order to change the orientation.
+ */
+ void swapLinks(int ind1) {
+ int ind2, ind3;
+
+ ind2 = list[ind1].next;
+ list[ind1].next = list[ind1].prev;
+ list[ind1].prev = ind2;
+ ind3 = ind2;
+ while (ind2 != ind1) {
+ ind3 = list[ind2].next;
+ list[ind2].next = list[ind2].prev;
+ list[ind2].prev = ind3;
+ ind2 = ind3;
+ }
+ }
+
+ // Methods for handling Triangle.
+
+ void storeTriangle(int i, int j, int k) {
+ /*
+ if (ccwLoop)
+ triangles.add(new Triangle(i,j,k));
+ else
+ triangles.add(new Triangle(j,i,k));
+ */
+
+ if(numTriangles >= maxNumTriangles) {
+ // System.out.println("Triangulator:storeTriangle Expanding triangle array..");
+ maxNumTriangles += INC_TRI_BK;
+ Triangle old[] = triangles;
+ triangles = new Triangle[maxNumTriangles];
+ if(old != null)
+ System.arraycopy(old, 0, triangles, 0, old.length);
+ }
+
+ if (ccwLoop)
+ triangles[numTriangles] = new Triangle(i,j,k);
+ else
+ triangles[numTriangles] = new Triangle(j,i,k);
+ numTriangles++;
+
+ }
+
+ // Methods for handling Point.
+
+ void initPnts(int number) {
+ if (maxNumPoints < number) {
+ maxNumPoints = number;
+ points = new Point2f[maxNumPoints];
+ }
+
+ for(int i = 0; i<number; i++)
+ points[i] = new Point2f(0.0f, 0.0f);
+
+ numPoints = 0;
+ }
+
+ boolean inPointsList(int index) {
+ return ((index >= 0) && (index < numPoints) &&
+ (numPoints <= maxNumPoints));
+ }
+
+ int storePoint(double x, double y) {
+ int i;
+
+ if (numPoints >= maxNumPoints) {
+ // System.out.println("Triangulator:storePoint Expanding points array ...");
+ maxNumPoints += INC_POINT_BK;
+ Point2f old[] = points;
+ points = new Point2f[maxNumPoints];
+ if(old != null)
+ System.arraycopy(old, 0, points, 0, old.length);
+ }
+
+ points[numPoints] = new Point2f((float)x, (float)y);
+ // points[numPoints].x = (float)x;
+ // points[numPoints].y = (float)y;
+ i = numPoints;
+ ++numPoints;
+
+ return i;
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/image/TextureLoader.java b/src/classes/share/com/sun/j3d/utils/image/TextureLoader.java
new file mode 100644
index 0000000..80ac782
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/image/TextureLoader.java
@@ -0,0 +1,779 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.image;
+
+import javax.media.j3d.*;
+import java.awt.Toolkit;
+import java.awt.Image;
+import java.awt.Component;
+import java.awt.image.*;
+import java.net.URL;
+import java.lang.reflect.Method;
+import java.awt.color.ColorSpace;
+import java.awt.Transparency;
+
+import java.awt.geom.AffineTransform;
+// TODO: remove the following for jdk1.2Beta4
+import java.awt.image.AffineTransformOp;
+// TODO: add the following for jdk1.2Beta4
+//import java.awt.image.AffineTransformOp;
+
+/**
+ * This class is used for loading a texture from an Image or BufferedImage.
+ * If Java Advanced Imaging is installed then it is used to load the
+ * images, otherwise AWT toolkit it used. JAI is capable of loading
+ * a larger set of image formats including .tiff and .bmp
+ *
+ * Methods are provided to retrieve the Texture object and the associated
+ * ImageComponent object or a scaled version of the ImageComponent object.
+ *
+ * Default format is RGBA. Other legal formats are: RGBA, RGBA4, RGB5_A1,
+ * RGB, RGB4, RGB5, R3_G3_B2, LUM8_ALPHA8, LUM4_ALPHA4, LUMINANCE and ALPHA
+ */
+
+public class TextureLoader extends Object {
+
+ /**
+ * Optional flag - specifies that mipmaps are generated for all levels
+ **/
+ public static final int GENERATE_MIPMAP = 0x01;
+
+ /**
+ * Optional flag - specifies that the ImageComponent2D will
+ * access the image data by reference
+ *
+ * @since Java 3D 1.2
+ **/
+ public static final int BY_REFERENCE = 0x02;
+
+ /**
+ * Optional flag - specifies that the ImageComponent2D will
+ * have a y-orientation of y up, meaning the orgin of the image is the
+ * lower left
+ *
+ * @since Java 3D 1.2
+ **/
+ public static final int Y_UP = 0x04;
+
+ /**
+ * Private declaration for BufferedImage allocation
+ */
+ private static ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+ private static int[] nBits = {8, 8, 8, 8};
+ private static int[] bandOffset = { 0, 1, 2, 3};
+ private static ComponentColorModel colorModel = new ComponentColorModel(cs, nBits, true, false, Transparency.TRANSLUCENT, 0);
+
+ private Texture2D tex = null;
+ private BufferedImage bufferedImage = null;
+ private ImageComponent2D imageComponent = null;
+ private int textureFormat = Texture.RGBA;
+ private int imageComponentFormat = ImageComponent.FORMAT_RGBA;
+ private int flags;
+ private boolean byRef;
+ private boolean yUp;
+
+ private boolean useJAI = false;
+
+ // JAI Methods
+ // Use introspection to get the methods so that JAI
+ // is not required to compile the Java3D API.
+ private Method jaiGetWidth;
+ private Method jaiGetHeight;
+ private Method jaiGetAsBufferedImage;
+ private Method jaiGetRendering;
+ private Method jaiCreate;
+ private boolean doneJAICheck = false;
+ private boolean jaiInstalled = false;
+
+ /**
+ * Contructs a TextureLoader object using the specified BufferedImage
+ * and default format RGBA
+ * @param bImage The BufferedImage used for loading the texture
+ */
+ public TextureLoader(BufferedImage bImage) {
+ this(bImage, new String("RGBA"), 0);
+ }
+
+ /**
+ * Contructs a TextureLoader object using the specified BufferedImage
+ * and format
+ * @param bImage The BufferedImage used for loading the texture
+ * @param format The format specifies which channels to use
+ */
+ public TextureLoader(BufferedImage bImage, String format) {
+ this(bImage, format, 0);
+ }
+
+ /**
+ * Contructs a TextureLoader object using the specified BufferedImage,
+ * option flags and default format RGBA
+ * @param bImage The BufferedImage used for loading the texture
+ * @param flags The flags specify what options to use in texture loading (generate mipmap etc)
+ */
+ public TextureLoader(BufferedImage bImage, int flags) {
+ this(bImage, new String("RGBA"), flags);
+ }
+
+ /**
+ * Contructs a TextureLoader object using the specified BufferedImage,
+ * format and option flags
+ * @param bImage The BufferedImage used for loading the texture
+ * @param format The format specifies which channels to use
+ * @param flags The flags specify what options to use in texture loading (generate mipmap etc)
+ */
+ public TextureLoader(BufferedImage bImage, String format, int flags) {
+ parseFormat(format);
+ this.flags = flags;
+ bufferedImage = bImage;
+
+ if ((flags & BY_REFERENCE) != 0) {
+ byRef = true;
+ }
+ if ((flags & Y_UP) != 0) {
+ yUp = true;
+ }
+ }
+
+
+ /**
+ * Contructs a TextureLoader object using the specified Image
+ * and default format RGBA
+ * @param image The Image used for loading the texture
+ * @param observer The associated image observer
+ */
+ public TextureLoader(Image image, Component observer) {
+ this(image, new String("RGBA"), 0, observer);
+ }
+
+ /**
+ * Contructs a TextureLoader object using the specified Image
+ * and format
+ * @param image The Image used for loading the texture
+ * @param format The format specifies which channels to use
+ * @param observer The associated image observer
+ */
+ public TextureLoader(Image image, String format, Component observer) {
+ this(image, format, 0, observer);
+ }
+
+ /**
+ * Contructs a TextureLoader object using the specified Image
+ * flags and default format RGBA
+ * @param image The Image used for loading the texture
+ * @param flags The flags specify what options to use in texture loading (generate mipmap etc)
+ * @param observer The associated image observer
+ */
+ public TextureLoader(Image image, int flags, Component observer) {
+ this(image, new String("RGBA"), flags, observer);
+ }
+
+ /**
+ * Contructs a TextureLoader object using the specified Image
+ * format and option flags
+ * @param image The Image used for loading the texture
+ * @param format The format specifies which channels to use
+ * @param flags The flags specify what options to use in texture loading (generate mipmap etc)
+ * @param observer The associated image observer
+ */
+ public TextureLoader(Image image, String format, int flags,
+ Component observer) {
+
+ if (observer == null) {
+ observer = new java.awt.Container();
+ }
+
+ parseFormat(format);
+ this.flags = flags;
+ bufferedImage = createBufferedImage(image, observer);
+
+ if ((flags & BY_REFERENCE) != 0) {
+ byRef = true;
+ }
+ if ((flags & Y_UP) != 0) {
+ yUp = true;
+ }
+ }
+
+
+ /**
+ * Contructs a TextureLoader object using the specified file
+ * and default format RGBA
+ * @param fname The file that specifies an Image to load the texture with
+ * @param observer The associated image observer
+ */
+ public TextureLoader(String fname, Component observer) {
+ this(fname, new String("RGBA"), 0, observer);
+ }
+
+ /**
+ * Contructs a TextureLoader object using the specified file,
+ * and format
+ * @param fname The file that specifies an Image to load the texture with
+ * @param format The format specifies which channels to use
+ * @param observer The associated image observer
+ */
+ public TextureLoader(String fname, String format, Component observer) {
+ this(fname, format, 0, observer);
+ }
+
+ /**
+ * Contructs a TextureLoader object using the specified file,
+ * option flags and default format RGBA
+ * @param fname The file that specifies an Image to load the texture with
+ * @param flags The flags specify what options to use in texture loading (generate mipmap etc)
+ * @param observer The associated image observer
+ */
+ public TextureLoader(String fname, int flags, Component observer) {
+ this(fname, new String("RGBA"), flags, observer);
+ }
+
+ /**
+ * Contructs a TextureLoader object using the specified file,
+ * format and option flags
+ * @param fname The file that specifies an Image to load the texture with
+ * @param format The format specifies which channels to use
+ * @param flags The flags specify what options to use in texture loading (generate mipmap etc)
+ * @param observer The associated image observer
+ */
+ public TextureLoader(String fname, String format, int flags,
+ Component observer) {
+
+ if (observer == null) {
+ observer = new java.awt.Container();
+ }
+
+ final Toolkit toolkit = Toolkit.getDefaultToolkit();
+ final Image image[] = new Image[1];
+ final String fn = fname;
+
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ image[0] = toolkit.createImage(fn);
+ return null;
+ }
+ }
+ );
+ parseFormat(format);
+ this.flags = flags;
+ bufferedImage = createBufferedImage(image[0], observer);
+
+ if (bufferedImage==null && JAIInstalled() ) {
+ parseFormat(format);
+ bufferedImage = JAIgetImage( fname, observer );
+ }
+
+ if (bufferedImage==null)
+ System.err.println("Error loading Image "+fname );
+
+ if ((flags & BY_REFERENCE) != 0) {
+ byRef = true;
+ }
+ if ((flags & Y_UP) != 0) {
+ yUp = true;
+ }
+ }
+
+ /**
+ * Load the image using the JAI API
+ */
+ private BufferedImage JAIgetImage( String filename, Component observer ) {
+ final String fn = filename;
+ BufferedImage bImage = null;
+
+ try {
+ Object source = java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ try {
+ Object renderedOp;
+ renderedOp = jaiCreate.invoke( null, new Object[] { "fileload", fn } );
+ // Force JAI to actually load the image
+ // within this privileged action
+ jaiGetRendering.invoke( renderedOp, new Object[] {} );
+
+ return renderedOp;
+ } catch( Exception e ) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+ }
+ );
+
+ int width = ((Integer)jaiGetWidth.invoke( source, new Object[] {} )).intValue();
+ int height = ((Integer)jaiGetHeight.invoke( source, new Object[] {} )).intValue();
+
+
+ WritableRaster wr = java.awt.image.Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, height , width * 4, 4, bandOffset, null);;
+ bImage = new BufferedImage(colorModel, wr, false, null);
+
+ java.awt.Graphics g = bImage.getGraphics();
+ BufferedImage im = (BufferedImage)jaiGetAsBufferedImage.invoke( source, new Object[] {} );
+ g.drawImage(im, 0, 0, observer );
+ } catch( Exception e ) {
+ return null;
+ //System.out.println("Unable to load Texture "+filename);
+ }
+
+
+ return bImage;
+ }
+
+ /**
+ * Load the image using the JAI API
+ */
+ private BufferedImage JAIgetImage( URL url, Component observer ) {
+ final URL local_url = url;
+ BufferedImage bImage=null;
+ try {
+ Object source = java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ try {
+ Object renderedOp;
+ renderedOp = jaiCreate.invoke( null, new Object[] { "URL", local_url } );
+ // Force JAI to actually load the image
+ // within this privileged action
+ jaiGetRendering.invoke( renderedOp, new Object[] {} );
+
+ return renderedOp;
+ } catch( Exception e ) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+ }
+ );
+
+
+ int width = ((Integer)jaiGetWidth.invoke( source, new Object[] {} )).intValue();
+ int height = ((Integer)jaiGetHeight.invoke( source, new Object[] {} )).intValue();
+
+
+ WritableRaster wr = java.awt.image.Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, height , width * 4, 4, bandOffset, null);;
+ bImage = new BufferedImage(colorModel, wr, false, null);
+
+ java.awt.Graphics g = bImage.getGraphics();
+ BufferedImage im = (BufferedImage)jaiGetAsBufferedImage.invoke( source, new Object[] {} );
+ g.drawImage(im, 0, 0, observer );
+ } catch( Exception e ) {
+ return null;
+ //System.out.println("Unable to load Texture "+local_url.toString());
+ }
+
+ return bImage;
+ }
+
+ /**
+ * Check if JAI is installed
+ * If it is installed created Methods for each method we need
+ * to call
+ */
+ private boolean JAIInstalled() {
+ if (!doneJAICheck) {
+ try {
+ Class cl = Class.forName("javax.media.jai.PlanarImage");
+ jaiGetWidth = cl.getDeclaredMethod( "getWidth", new Class[] {} );
+ jaiGetHeight = cl.getDeclaredMethod( "getHeight", new Class[] {} );
+ jaiGetAsBufferedImage = cl.getDeclaredMethod( "getAsBufferedImage", new Class[] {} );
+ Class cl2 = Class.forName("javax.media.jai.JAI");
+ jaiCreate = cl2.getDeclaredMethod( "create", new Class[] { String.class, Object.class } );
+ Class cl3 = Class.forName("javax.media.jai.RenderedOp");
+ jaiGetRendering = cl3.getDeclaredMethod("getRendering", new Class[] {} );
+
+ jaiInstalled = true;
+ } catch( ClassNotFoundException e ) {
+ jaiInstalled = false;
+ } catch( NoSuchMethodException ex ) {
+ ex.printStackTrace();
+ jaiInstalled = false;
+ }
+
+ doneJAICheck = true;
+ }
+
+ return jaiInstalled;
+ }
+
+
+ /**
+ * Contructs a TextureLoader object using the specified URL
+ * and default format RGBA
+ * @param url The URL that specifies an Image to load the texture with
+ * @param observer The associated image observer
+ */
+ public TextureLoader(URL url, Component observer) {
+ this(url, new String("RGBA"), 0, observer);
+ }
+
+ /**
+ * Contructs a TextureLoader object using the specified URL,
+ * and format
+ * @param url The URL that specifies an Image to load the texture with
+ * @param format The format specifies which channels to use
+ * @param observer The associated image observer
+ */
+ public TextureLoader(URL url, String format, Component observer) {
+ this(url, format, 0, observer);
+ }
+
+ /**
+ * Contructs a TextureLoader object using the specified URL,
+ * option flags and default format RGBA
+ * @param url The URL that specifies an Image to load the texture with
+ * @param flags The flags specify what options to use in texture loading (generate mipmap etc)
+ * @param observer The associated image observer
+ */
+ public TextureLoader(URL url, int flags, Component observer) {
+ this(url, new String("RGBA"), flags, observer);
+ }
+ /**
+ * Contructs a TextureLoader object using the specified URL,
+ * format and option flags
+ * @param url The url that specifies an Image to load the texture with
+ * @param format The format specifies which channels to use
+ * @param flags The flags specify what options to use in texture loading (generate mipmap etc)
+ * @param observer The associated image observer
+ */
+ public TextureLoader(URL url, String format, int flags,
+ Component observer) {
+
+ if (observer == null) {
+ observer = new java.awt.Container();
+ }
+
+ final Toolkit toolkit = Toolkit.getDefaultToolkit();
+ final Image image[] = new Image[1];
+ final URL Url = url;
+
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ image[0] = toolkit.getImage(Url);
+ return null;
+ }
+ }
+ );
+ parseFormat(format);
+ this.flags = flags;
+ bufferedImage = createBufferedImage(image[0], observer);
+
+ if (bufferedImage==null && JAIInstalled() ) {
+ bufferedImage = JAIgetImage( url, observer );
+ }
+
+ if (bufferedImage==null)
+ System.err.println("Error loading Image "+url.toString() );
+
+ if ((flags & BY_REFERENCE) != 0) {
+ byRef = true;
+ }
+ if ((flags & Y_UP) != 0) {
+ yUp = true;
+ }
+ }
+
+
+ /**
+ * Returns the associated ImageComponent2D object
+ *
+ * @return The associated ImageComponent2D object
+ */
+ public ImageComponent2D getImage() {
+
+ if (imageComponent == null)
+ imageComponent = new ImageComponent2D(imageComponentFormat,
+ bufferedImage, byRef, yUp);
+ return imageComponent;
+ }
+
+ /**
+ * Returns the scaled ImageComponent2D object
+ *
+ * @param xScale The X scaling factor
+ * @param yScale The Y scaling factor
+ *
+ * @return The scaled ImageComponent2D object
+ */
+ public ImageComponent2D getScaledImage(float xScale, float yScale) {
+
+ if (xScale == 1.0f && yScale == 1.0f)
+ return getImage();
+ else
+ return(new ImageComponent2D(imageComponentFormat,
+ getScaledImage(bufferedImage,
+ xScale, yScale),
+ byRef, yUp));
+ }
+
+ /**
+ * Returns the scaled ImageComponent2D object
+ *
+ * @param width The desired width
+ * @param height The desired height
+ *
+ * @return The scaled ImageComponent2D object
+ */
+ public ImageComponent2D getScaledImage(int width, int height) {
+
+ if (bufferedImage.getWidth() == width &&
+ bufferedImage.getHeight() == height)
+ return getImage();
+ else
+ return(new ImageComponent2D(imageComponentFormat,
+ getScaledImage(bufferedImage,
+ width, height),
+ byRef, yUp));
+ }
+
+ /**
+ * Returns the associated Texture object
+ * or null if the image failed to load
+ *
+ * @return The associated Texture object
+ */
+ public Texture getTexture() {
+
+ ImageComponent2D[] scaledImageComponents = null;
+ BufferedImage[] scaledBufferedImages = null;
+ if (tex == null) {
+ if (bufferedImage==null) return null;
+
+ int width = getClosestPowerOf2(bufferedImage.getWidth());
+ int height = getClosestPowerOf2(bufferedImage.getHeight());
+
+ if ((flags & GENERATE_MIPMAP) != 0) {
+
+ BufferedImage origImage = bufferedImage;
+ int newW = width;
+ int newH = height;
+ int level = Math.max(computeLog(width), computeLog(height)) + 1;
+ scaledImageComponents = new ImageComponent2D[level];
+ scaledBufferedImages = new BufferedImage[level];
+ tex = new Texture2D(tex.MULTI_LEVEL_MIPMAP, textureFormat,
+ width, height);
+
+ for (int i = 0; i < level; i++) {
+ scaledBufferedImages[i] = getScaledImage(origImage, newW, newH);
+ scaledImageComponents[i] = new ImageComponent2D(
+ imageComponentFormat, scaledBufferedImages[i],
+ byRef, yUp);
+
+ tex.setImage(i, scaledImageComponents[i]);
+ if (newW > 1) newW >>= 1;
+ if (newH > 1) newH >>= 1;
+ origImage = scaledBufferedImages[i];
+ }
+
+ } else {
+ scaledImageComponents = new ImageComponent2D[1];
+ scaledBufferedImages = new BufferedImage[1];
+
+ // Create texture from image
+ scaledBufferedImages[0] = getScaledImage(bufferedImage,
+ width, height);
+ scaledImageComponents[0] = new ImageComponent2D(
+ imageComponentFormat, scaledBufferedImages[0],
+ byRef, yUp);
+
+ tex = new Texture2D(tex.BASE_LEVEL, textureFormat, width, height);
+
+ tex.setImage(0, scaledImageComponents[0]);
+ }
+ tex.setMinFilter(tex.BASE_LEVEL_LINEAR);
+ tex.setMagFilter(tex.BASE_LEVEL_LINEAR);
+ }
+
+ return tex;
+ }
+
+ // create a BufferedImage from an Image object
+ private BufferedImage createBufferedImage(Image image, Component observer) {
+
+ int status;
+
+ observer.prepareImage(image, null);
+ while(true) {
+ status = observer.checkImage(image, null);
+ if ((status & ImageObserver.ERROR) != 0) {
+ return null;
+ } else if ((status & ImageObserver.ALLBITS) != 0) {
+ break;
+ }
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {}
+ }
+
+ int width = image.getWidth(observer);
+ int height = image.getHeight(observer);
+
+ WritableRaster wr = java.awt.image.Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, height , width * 4, 4, bandOffset, null);;
+ BufferedImage bImage = new BufferedImage(colorModel, wr, false, null);
+
+ java.awt.Graphics g = bImage.getGraphics();
+ g.drawImage(image, 0, 0, observer);
+
+ return bImage;
+ }
+
+ // initialize appropriate format for ImageComponent and Texture
+ private void parseFormat(String format) {
+ if (format.equals("RGBA")) {
+ imageComponentFormat = ImageComponent.FORMAT_RGBA;
+ textureFormat = Texture.RGBA;
+
+ } else if (format.equals("RGBA4")) {
+ imageComponentFormat = ImageComponent.FORMAT_RGBA4;
+ textureFormat = Texture.RGBA;
+
+ } else if (format.equals("RGB5_A1")) {
+ imageComponentFormat = ImageComponent.FORMAT_RGB5_A1;
+ textureFormat = Texture.RGBA;
+
+ } else if (format.equals("RGB")) {
+ imageComponentFormat = ImageComponent.FORMAT_RGB;
+ textureFormat = Texture.RGB;
+
+ } else if (format.equals("RGB4")) {
+ imageComponentFormat = ImageComponent.FORMAT_RGB4;
+ textureFormat = Texture.RGB;
+
+ } else if (format.equals("RGB5")) {
+ imageComponentFormat = ImageComponent.FORMAT_RGB5;
+ textureFormat = Texture.RGB;
+
+ } else if (format.equals("R3_G3_B2")) {
+ imageComponentFormat = ImageComponent.FORMAT_R3_G3_B2;
+ textureFormat = Texture.RGB;
+
+ } else if (format.equals("LUM8_ALPHA8")) {
+ imageComponentFormat = ImageComponent.FORMAT_LUM8_ALPHA8;
+ textureFormat = Texture.LUMINANCE_ALPHA;
+
+ } else if (format.equals("LUM4_ALPHA4")) {
+ imageComponentFormat = ImageComponent.FORMAT_LUM4_ALPHA4;
+ textureFormat = Texture.LUMINANCE_ALPHA;
+
+ } else if (format.equals("LUMINANCE")) {
+ imageComponentFormat = ImageComponent.FORMAT_CHANNEL8;
+ textureFormat = Texture.LUMINANCE;
+
+ } else if (format.equals("ALPHA")) {
+ imageComponentFormat = ImageComponent.FORMAT_CHANNEL8;
+ textureFormat = Texture.ALPHA;
+ }
+ }
+
+ // return a scaled image of given width and height
+ private BufferedImage getScaledImage(BufferedImage origImage, int width,
+ int height){
+
+ int origW = origImage.getWidth();
+ int origH = origImage.getHeight();
+ float xScale = (float)width/(float)origW;
+ float yScale = (float)height/(float)origH;
+
+ return (getScaledImage(origImage, xScale, yScale));
+ }
+
+ // return a scaled image of given x and y scale
+ private BufferedImage getScaledImage(BufferedImage origImage, float xScale,
+ float yScale){
+
+ // If the image is already the requested size, no need to scale
+ if (xScale == 1.0f && yScale == 1.0f)
+ return origImage;
+ else {
+
+ int scaleW = (int)(origImage.getWidth() * xScale + 0.5);
+ int scaleH = (int)(origImage.getHeight() * yScale + 0.5);
+ WritableRaster wr = java.awt.image.Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, scaleW, scaleH , scaleW * 4, 4, bandOffset, null);;
+ BufferedImage scaledImage = new BufferedImage(colorModel, wr, false, null);
+
+ java.awt.Graphics2D g2 = scaledImage.createGraphics();
+ AffineTransform at = AffineTransform.getScaleInstance(xScale,
+ yScale);
+ g2.transform(at);
+ g2.drawImage(origImage, 0, 0, null);
+
+ return scaledImage;
+ }
+ }
+
+
+ private int computeLog(int value) {
+ int i = 0;
+
+ if (value == 0) return -1;
+ for (;;) {
+ if (value == 1)
+ return i;
+ value >>= 1;
+ i++;
+ }
+ }
+
+ private int getClosestPowerOf2(int value) {
+
+ if (value < 1)
+ return value;
+
+ int powerValue = 1;
+ for (;;) {
+ powerValue *= 2;
+ if (value < powerValue) {
+ // Found max bound of power, determine which is closest
+ int minBound = powerValue/2;
+ if ((powerValue - value) >
+ (value - minBound))
+ return minBound;
+ else
+ return powerValue;
+ }
+ }
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/picking/PickCanvas.java b/src/classes/share/com/sun/j3d/utils/picking/PickCanvas.java
new file mode 100644
index 0000000..9ec1183
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/picking/PickCanvas.java
@@ -0,0 +1,248 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.picking;
+
+import java.awt.event.*;
+import javax.vecmath.*;
+import javax.media.j3d.*;
+import com.sun.j3d.utils.geometry.*; // Cone, Cylinder
+
+/**
+ * A subclass of PickTool, simplifies picking using mouse events from a canvas.
+ * This class allows picking using canvas x,y locations by generating the
+ * appropriate pick shape.
+ * <p>
+ * The pick tolerance specifies the distance from the
+ * pick center to include in the pick shape. A tolerance of 0.0 may speedup
+ * picking slightly, but also make it very difficult to pick points and lines.
+ * <p>
+ * The pick canvas can be used to make a series of picks. For example, to
+ * initialize the pick canvas:
+ * <blockquote><pre>
+ * PickCanvas pickCanvas = new PickCanvas(canvas, scene);
+ * pickCanvas.setMode(PickTool.GEOMETRY_INTERSECT_INFO);
+ * pickCanvas.setTolerance(4.0f);
+ * </pre></blockquote>
+ * <p>
+ * Then for each mouse event:
+ * <blockquote><pre>
+ * pickCanvas.setShapeLocation(mouseEvent);
+ * PickResult[] results = pickCanvas.pickAll();
+ * </pre></blockquote>
+ * <p>
+ * NOTE: For the pickAllSorted or pickClosest methods, the picks will be sorted
+ * by the distance from the ViewPlatform to the intersection point.
+ * @see PickTool
+ */
+public class PickCanvas extends PickTool {
+
+ /* OPEN ISSUES:
+ -- Should restrict the pick shape to the front/back clip plane
+ */
+
+
+ /** The canvas we are picking into */
+ Canvas3D canvas;
+
+ /* the pick tolerance, default to 2.0 */
+ float tolerance = 2.0f;
+ int save_xpos;
+ int save_ypos;
+
+ /** Constructor with Canvas3D for mouse events and BranchGroup to be picked.
+ */
+ public PickCanvas (Canvas3D c, BranchGroup b) {
+ super (b);
+ canvas = c;
+ }
+
+ /** Constructor with Canvas3D for mouse events and Locale to be picked.
+ */
+ public PickCanvas (Canvas3D c, Locale l) {
+ super (l);
+ canvas = c;
+ }
+
+ /** Inquire the canvas to be used for picking operations.
+ @return the canvas.
+ */
+ public Canvas3D getCanvas() {
+ return canvas;
+ }
+
+ /** Set the picking tolerance. Objects within this distance
+ * (in pixels)
+ * to the mouse x,y location will be picked. The default tolerance is 2.0.
+ * @param t The tolerance
+ * @exception IllegalArgumentException if the tolerance is less than 0.
+ */
+ public void setTolerance(float t) {
+ if (t < 0.0f) {
+ throw new IllegalArgumentException();
+ }
+ tolerance = t;
+
+ if ((pickShape != null) && (!userDefineShape)) {
+ // reset pickShape
+ pickShape = null;
+ setShapeLocation(save_xpos, save_ypos);
+ }
+ }
+
+ /** Get the pick tolerance.
+ */
+ public float getTolerance() {
+ return tolerance;
+ }
+
+ /** Set the pick location. Defines the location on the canvas where the
+ pick is to be performed.
+ @param mevent The MouseEvent for the picking point
+ */
+ public void setShapeLocation(MouseEvent mevent) {
+ setShapeLocation(mevent.getX(), mevent.getY());
+ }
+ /** Set the pick location. Defines the location on the canvas where the
+ pick is to be performed (upper left corner of canvas is 0,0).
+ @param xpos the X position of the picking point
+ @param ypos the Y position of the picking point
+ */
+ public void setShapeLocation (int xpos, int ypos) {
+ Transform3D motion = new Transform3D();
+ Point3d eyePosn = new Point3d();
+ Point3d mousePosn = new Point3d();
+ Vector3d mouseVec = new Vector3d();
+ boolean isParallel = false;
+ double radius = 0.0;
+ double spreadAngle = 0.0;
+
+ this.save_xpos = xpos;
+ this.save_ypos = ypos;
+ canvas.getCenterEyeInImagePlate(eyePosn);
+ canvas.getPixelLocationInImagePlate(xpos,ypos,mousePosn);
+
+ if ((canvas.getView() != null) &&
+ (canvas.getView().getProjectionPolicy() ==
+ View.PARALLEL_PROJECTION)) {
+ // Correct for the parallel projection: keep the eye's z
+ // coordinate, but make x,y be the same as the mouse, this
+ // simulates the eye being at "infinity"
+ eyePosn.x = mousePosn.x;
+ eyePosn.y = mousePosn.y;
+ isParallel = true;
+ }
+
+ // Calculate radius for PickCylinderRay and spread angle for PickConeRay
+ Vector3d eyeToCanvas = new Vector3d();
+ eyeToCanvas.sub (mousePosn, eyePosn);
+ double distanceEyeToCanvas = eyeToCanvas.length();
+
+ Point3d deltaImgPlate = new Point3d();
+ canvas.getPixelLocationInImagePlate (xpos+1, ypos, deltaImgPlate);
+
+ Vector3d ptToDelta = new Vector3d();
+ ptToDelta.sub (mousePosn, deltaImgPlate);
+ double distancePtToDelta = ptToDelta.length();
+ distancePtToDelta *= tolerance;
+
+ canvas.getImagePlateToVworld(motion);
+
+ /*
+ System.out.println("mouse position " + xpos + " " + ypos);
+ System.out.println("before, mouse " + mousePosn + " eye " + eyePosn);
+ */
+
+ motion.transform(eyePosn);
+ start = new Point3d (eyePosn); // store the eye position
+ motion.transform(mousePosn);
+ mouseVec.sub(mousePosn, eyePosn);
+ mouseVec.normalize();
+
+ /*
+ System.out.println(motion + "\n");
+ System.out.println("after, mouse " + mousePosn + " eye " + eyePosn +
+ " mouseVec " + mouseVec);
+ */
+
+ if (tolerance == 0.0) {
+ if ((pickShape != null) && (pickShape instanceof PickRay)) {
+ ((PickRay)pickShape).set (eyePosn, mouseVec);
+ } else {
+ pickShape = (PickShape) new PickRay (eyePosn, mouseVec);
+ }
+ // pickShape = (PickShape) new PickConeRay (eyePosn,
+ // mouseVec,1.0*Math.PI/180.0);
+ } else {
+ if (isParallel) {
+ // Parallel projection, use a PickCylinderRay
+ distancePtToDelta *= motion.getScale();
+ if ((pickShape != null) &&
+ (pickShape instanceof PickCylinderRay)) {
+ ((PickCylinderRay)pickShape).set (eyePosn, mouseVec,
+ distancePtToDelta);
+ } else {
+ pickShape = (PickShape) new PickCylinderRay (eyePosn,
+ mouseVec, distancePtToDelta);
+ }
+ } else {
+ // Perspective projection, use a PickConeRay
+
+ // Calculate spread angle
+ spreadAngle = Math.atan (distancePtToDelta/distanceEyeToCanvas);
+
+ if ((pickShape != null) &&
+ (pickShape instanceof PickConeRay)) {
+ ((PickConeRay)pickShape).set (eyePosn, mouseVec,
+ spreadAngle);
+ } else {
+ pickShape = (PickShape) new PickConeRay (eyePosn, mouseVec,
+ spreadAngle);
+ }
+ }
+ }
+ }
+} // PickCanvas
+
+
diff --git a/src/classes/share/com/sun/j3d/utils/picking/PickIntersection.java b/src/classes/share/com/sun/j3d/utils/picking/PickIntersection.java
new file mode 100644
index 0000000..5aaa229
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/picking/PickIntersection.java
@@ -0,0 +1,1570 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.picking;
+
+import javax.vecmath.*;
+import javax.media.j3d.*;
+import com.sun.j3d.utils.geometry.Primitive;
+
+/**
+ * Holds information about an intersection of a PickShape with a Node
+ * as part of a PickResult. Information about
+ * the intersected geometry, intersected primitive, intersection point, and
+ * closest vertex can be inquired.
+ * <p>
+ * The intersected geometry is indicated by an index into the list of
+ * geometry arrays on the PickResult. It can also be inquired from this
+ * object.
+ * <p>
+ * The intersected primitive indicates which primitive out of the GeometryArray
+ * was intersected (where the primitive is a point, line, triangle or quad,
+ * not a
+ * <code>com.sun.j3d.utils.geometry.Primitive)</code>.
+ * For example, the intersection would indicate which triangle out of a
+ * triangle strip was intersected.
+ * The methods which return primitive data will have one value if the primitive
+ * is
+ * a point, two values if the primitive is a line, three values if the primitive
+ * is a triangle and four values if the primitive is quad.
+ * <p>
+ * The primitive's VWorld coordinates are saved when then intersection is
+ * calculated. The local coordinates, normal, color and texture coordinates
+ * for the primitive can also be inquired if they are present and readable.
+ * <p>
+ * The intersection point is the location on the primitive which intersects the
+ * pick shape closest to the center of the pick shape. The intersection point's
+ * location in VWorld coordinates is saved when the intersection is calculated.
+ * The local coordinates, normal, color and texture coordiantes of at the
+ * intersection can be interpolated if they are present and readable.
+ * <p>
+ * The closest vertex is the vertex of the primitive closest to the intersection
+ * point. The vertex index, VWorld coordinates and local coordinates of the
+ * closest vertex can be inquired. The normal, color and texture coordinate
+ * of the closest vertex can be inquired from the geometry array:
+ * <p><blockquote><pre>
+ * Vector3f getNormal(PickIntersection pi, int vertexIndex) {
+ * int index;
+ * Vector3d normal = new Vector3f();
+ * GeometryArray ga = pickIntersection.getGeometryArray();
+ * if (pickIntersection.geometryIsIndexed()) {
+ * index = ga.getNormalIndex(vertexIndex);
+ * } else {
+ * index = vertexIndex;
+ * }
+ * ga.getNormal(index, normal);
+ * return normal;
+ * }
+ * </pre></blockquote>
+ * <p>
+ * The color, normal
+ * and texture coordinate information for the intersected primitive and the
+ * intersection point
+ * can be inquired
+ * the geometry includes them and the corresponding READ capibility bits are
+ * set.
+ * <A HREF="PickTool.html#setCapabilities(javax.media.j3d.Node, int)">
+ * <code>PickTool.setCapabilties(Node, int)</code></A>
+ * can be used to set the capability bits
+ * to allow this data to be inquired.
+ */
+public class PickIntersection {
+
+ /* OPEN ISSUES:
+ -- Tex coordinates always use texCoordSet == 0.
+ */
+
+ /* =================== ATTRIBUTES ======================= */
+
+
+ // init by constructor:
+
+ /** PickResult for intersection is part of */
+ PickResult pickResult = null;
+
+ // init by intersection:
+ /** Distance between start point and intersection point (see comment above)*/
+ double distance = -1;
+
+ /** index of GeometryArray in PickResult */
+ int geomIndex = 0;
+
+ /** Indices of the intersected primitive */
+ int[] primitiveVertexIndices = null;
+
+ /** VWorld coordinates of intersected primitive */
+ Point3d[] primitiveCoordinatesVW = null;
+
+ /** VWorld Coordinates of the intersection point */
+ Point3d pointCoordinatesVW = null;
+
+ // Derived data
+
+ // Geometry
+ GeometryArray geom = null;
+ IndexedGeometryArray iGeom = null;
+ boolean hasNormals = false;
+ boolean hasColors = false;
+ boolean hasTexCoords = false;
+
+ // Primitive
+ /* indices for the different data types */
+ int[] primitiveCoordinateIndices;
+ int[] primitiveNormalIndices;
+ int[] primitiveColorIndices;
+ int[] primitiveTexCoordIndices;
+
+
+ /* Local coordinates of the intersected primitive */
+ Point3d[] primitiveCoordinates = null;
+
+ /* Normals of the intersected primitive */
+ Vector3f[] primitiveNormals = null;
+
+ /* Colors of the intersected primitive */
+ Color4f[] primitiveColors = null;
+
+ /* TextureCoordinates of the intersected primitive */
+ TexCoord3f[] primitiveTexCoords = null;
+
+ // Intersection point
+
+ /** Local Coordinates of the intersection point */
+ Point3d pointCoordinates = null;
+
+ /** Normal at the intersection point */
+ Vector3f pointNormal = null;
+
+ /** Color at the intersection point */
+ Color4f pointColor = null;
+
+ /** TexCoord at the intersection point */
+ TexCoord3f pointTexCoord = null;
+
+ // Closest Vertex
+ /** Index of the closest vertex */
+ int closestVertexIndex = -1;
+
+ /** Coordinates of the closest vertex */
+ Point3d closestVertexCoordinates = null;
+
+ /** Coordinates of the closest vertex (World coordinates) */
+ Point3d closestVertexCoordinatesVW = null;
+
+ /** Weight factors for interpolation, values correspond to vertex indices,
+ * sum == 1
+ */
+ double[] interpWeights;
+
+ static final boolean debug = false;
+
+ // Axis constants
+ static final int X_AXIS = 1;
+ static final int Y_AXIS = 2;
+ static final int Z_AXIS = 3;
+
+ // Tolerance for numerical stability
+ static final double TOL = 1.0e-5;
+
+ /* =================== METHODS ======================= */
+
+ /** Constructor
+ @param pickResult The pickResult this intersection is part of.
+ */
+ PickIntersection (PickResult pr, GeometryArray geomArr) {
+
+ // pr can't be null.
+ pickResult = pr;
+ geom = geomArr;
+ if (geom == null) {
+ GeometryArray[] ga = pickResult.getGeometryArrays();
+ geom = ga[geomIndex];
+
+ }
+
+ if (geom instanceof IndexedGeometryArray) {
+ iGeom = (IndexedGeometryArray)geom;
+ }
+ int vertexFormat = geom.getVertexFormat();
+ hasColors = (0 != (vertexFormat &
+ (GeometryArray.COLOR_3 | GeometryArray.COLOR_4)));
+ hasNormals = (0 != (vertexFormat & GeometryArray.NORMALS));
+ hasTexCoords = (0 != (vertexFormat &
+ (GeometryArray.TEXTURE_COORDINATE_2 |
+ GeometryArray.TEXTURE_COORDINATE_3)));
+ }
+
+ /**
+ String representation of this object
+ */
+ public String toString () {
+ String rt = new String ("PickIntersection: ");
+ rt += " pickResult = "+pickResult + "\n";
+ rt += " geomIndex = "+geomIndex + "\n";
+ if (distance != -1) rt += " dist:"+distance + "\n";
+ if (pointCoordinates != null) rt += " pt:" + pointCoordinates + "\n";
+ if (pointCoordinatesVW != null) rt += " ptVW:" + pointCoordinatesVW + "\n";
+
+ if (primitiveCoordinateIndices != null) {
+ rt += " prim coordinate ind:" + "\n";
+ for (int i=0;i<primitiveCoordinateIndices.length;i++) {
+ rt += " "+primitiveCoordinateIndices[i] + "\n";
+ }
+ }
+
+ if (primitiveColorIndices != null) {
+ rt += " prim color ind:" + "\n";
+ for (int i=0;i<primitiveColorIndices.length;i++) {
+ rt += " "+primitiveColorIndices[i] + "\n";
+ }
+ }
+
+ if (primitiveNormalIndices != null) {
+ rt += " prim normal ind:" + "\n";
+ for (int i=0;i<primitiveNormalIndices.length;i++) {
+ rt += " "+primitiveNormalIndices[i] + "\n";
+ }
+ }
+
+ if (primitiveTexCoordIndices != null) {
+ rt += " prim texture ind:" + "\n";
+ for (int i=0;i<primitiveTexCoordIndices.length;i++) {
+ rt += " "+primitiveTexCoordIndices[i] + "\n";
+ }
+ }
+
+ if (closestVertexCoordinates != null) {
+ rt += " clos. vert:" + closestVertexCoordinates + "\n";
+ }
+
+ if (closestVertexCoordinatesVW != null) {
+ rt += " clos. vert:" + closestVertexCoordinatesVW + "\n";
+ }
+
+ if (closestVertexIndex != -1) {
+ rt += " clos. vert. ind.:" + closestVertexIndex + "\n";
+ }
+ return rt;
+ }
+
+ /* Call only by PickResult */
+ String toString2 () {
+ String rt = new String ("PickIntersection: ");
+ // rt += " pickResult = "+pickResult;
+ rt += " geomIndex = "+geomIndex + "\n";
+ if (distance != -1) rt += " dist:"+distance + "\n";
+ if (pointCoordinates != null) rt += " pt:" + pointCoordinates + "\n";
+ if (pointCoordinatesVW != null) rt += " ptVW:" + pointCoordinatesVW + "\n";
+
+ if (primitiveCoordinateIndices != null) {
+ rt += " prim coordinate ind:" + "\n";
+ for (int i=0;i<primitiveCoordinateIndices.length;i++) {
+ rt += " "+primitiveCoordinateIndices[i] + "\n";
+ }
+ }
+
+ if (primitiveColorIndices != null) {
+ rt += " prim color ind:" + "\n";
+ for (int i=0;i<primitiveColorIndices.length;i++) {
+ rt += " "+primitiveColorIndices[i] + "\n";
+ }
+ }
+
+ if (primitiveNormalIndices != null) {
+ rt += " prim normal ind:" + "\n";
+ for (int i=0;i<primitiveNormalIndices.length;i++) {
+ rt += " "+primitiveNormalIndices[i] + "\n";
+ }
+ }
+
+ if (primitiveTexCoordIndices != null) {
+ rt += " prim texture ind:" + "\n";
+ for (int i=0;i<primitiveTexCoordIndices.length;i++) {
+ rt += " "+primitiveTexCoordIndices[i] + "\n";
+ }
+ }
+
+ if (closestVertexCoordinates != null) {
+ rt += " clos. vert:" + closestVertexCoordinates + "\n";
+ }
+
+ if (closestVertexCoordinatesVW != null) {
+ rt += " clos. vert:" + closestVertexCoordinatesVW + "\n";
+ }
+
+ if (closestVertexIndex != -1) {
+ rt += " clos. vert. ind.:" + closestVertexIndex + "\n";
+ }
+ return rt;
+ }
+
+ /**
+ Gets the PickResult this intersection is part of
+ */
+ PickResult getPickResult() {
+ return pickResult;
+ }
+
+ /**
+ Sets the geom index into the pick result
+ */
+ void setGeomIndex(int gi) {
+
+ if (geomIndex != gi) {
+ GeometryArray[] ga = pickResult.getGeometryArrays();
+ geom = ga[gi];
+
+ if (geom instanceof IndexedGeometryArray) {
+ iGeom = (IndexedGeometryArray)geom;
+ }
+ int vertexFormat = geom.getVertexFormat();
+ hasColors = (0 != (vertexFormat &
+ (GeometryArray.COLOR_3 | GeometryArray.COLOR_4)));
+ hasNormals = (0 != (vertexFormat & GeometryArray.NORMALS));
+ hasTexCoords = (0 != (vertexFormat &
+ (GeometryArray.TEXTURE_COORDINATE_2 |
+ GeometryArray.TEXTURE_COORDINATE_3)));
+ }
+
+ geomIndex = gi;
+ }
+
+ /**
+ Sets the coordinates of the intersection point (world coordinates).
+ @param pt the coordinates
+ */
+ void setPointCoordinatesVW (Point3d pt) {
+ if (pointCoordinatesVW == null) {
+ pointCoordinatesVW = new Point3d ();
+ }
+ pointCoordinatesVW.x = pt.x;
+ pointCoordinatesVW.y = pt.y;
+ pointCoordinatesVW.z = pt.z;
+ }
+
+ /**
+ Returns the coordinates of the intersection point (world coordinates),
+ if available.
+ @return coordinates of the point
+ */
+ public Point3d getPointCoordinatesVW() {
+ return pointCoordinatesVW;
+ }
+
+
+ /**
+ Get the distance from the PickShape start point to the intersection point
+ @return the distance to the intersection point, if available.
+ */
+ public double getDistance () {
+ return distance;
+ }
+
+ /**
+ Set the distance to intersection point
+ @param dist the distance to the intersection point
+ */
+ void setDistance (double dist) {
+ distance = dist;
+ }
+
+
+ /** Set VWorld coordinates of the picked primtive
+ @param coords
+ */
+ void setPrimitiveCoordinatesVW (Point3d [] coords) {
+ primitiveCoordinatesVW = new Point3d [coords.length];
+ System.arraycopy (coords, 0, primitiveCoordinatesVW, 0, coords.length);
+ }
+
+ /**
+ Get VWorld coordinates of the intersected primitive
+ @return an array of Point3d's for the primitive that was picked
+ */
+ public Point3d[] getPrimitiveCoordinatesVW () {
+ return primitiveCoordinatesVW;
+ }
+
+
+ /** Set vertex indices of primitive's vertices
+ @param verts array of coordinate indices
+ */
+ void setVertexIndices (int [] verts) {
+ primitiveVertexIndices = new int [verts.length];
+ System.arraycopy (verts, 0, primitiveVertexIndices, 0, verts.length);
+ }
+
+ /**
+ Get vertex indices of the intersected primitive
+ @return an array which contains the list of indices
+ */
+ public int [] getPrimitiveVertexIndices () {
+ return primitiveVertexIndices;
+ }
+
+ /**
+ Returns the index of the intersected GeometryArray into the geometry
+ arrays in the PickResult
+ */
+ public int getGeometryArrayIndex() {
+ return geomIndex;
+ }
+
+ /* ================================================================== */
+ /* Derived Data: GeometryArray */
+ /* ================================================================== */
+
+ /** Returns the GeometryArray for the intersection */
+ public GeometryArray getGeometryArray() {
+ if (geom == null) {
+ GeometryArray[] ga = pickResult.getGeometryArrays();
+ geom = ga[geomIndex];
+ if (geom instanceof IndexedGeometryArray) {
+ iGeom = (IndexedGeometryArray)geom;
+ }
+ int vertexFormat = geom.getVertexFormat();
+ hasColors = (0 != (vertexFormat &
+ (GeometryArray.COLOR_3 | GeometryArray.COLOR_4)));
+ hasNormals = (0 != (vertexFormat & GeometryArray.NORMALS));
+ hasTexCoords = (0 != (vertexFormat &
+ (GeometryArray.TEXTURE_COORDINATE_2 |
+ GeometryArray.TEXTURE_COORDINATE_3)));
+ }
+ return geom;
+ }
+
+ /** Returns true if the geometry is indexed */
+ public boolean geometryIsIndexed() {
+ GeometryArray ga = getGeometryArray();
+ if (iGeom != null) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+
+ /* ================================================================== */
+ /* Derived Data: Closest Vertex */
+ /* ================================================================== */
+
+
+ /** Get coordinates of closest vertex (local)
+ @return the coordinates of the vertex closest to the intersection point
+ */
+ public Point3d getClosestVertexCoordinates () {
+ // System.out.println("closestVertexCoordinates " + closestVertexCoordinates);
+ if (closestVertexCoordinates == null) {
+ int vertexIndex = getClosestVertexIndex();
+ int vformat = geom.getVertexFormat();
+ int val;
+
+ int[] indices = getPrimitiveCoordinateIndices();
+ if ((vformat & GeometryArray.BY_REFERENCE) == 0) {
+ closestVertexCoordinates = new Point3d();
+ geom.getCoordinate(indices[vertexIndex], closestVertexCoordinates);
+ /* System.out.println("PickIntersection : closestVertexCoordinates " +
+ closestVertexCoordinates + " vertexIndex " +
+ vertexIndex);
+ */
+ }
+ else {
+ if ((vformat & GeometryArray.INTERLEAVED) == 0) {
+ double[] doubleData = geom.getCoordRefDouble();
+ // If data was set as float then ..
+ if (doubleData == null) {
+ float[] floatData = geom.getCoordRefFloat();
+ if (floatData == null) {
+ Point3f[] p3fData = geom.getCoordRef3f();
+ if (p3fData == null) {
+ Point3d[] p3dData = geom.getCoordRef3d();
+ closestVertexCoordinates = new Point3d(p3dData[indices[vertexIndex]].x, p3dData[indices[vertexIndex]].y, p3dData[indices[vertexIndex]].z);
+ }
+ else {
+ closestVertexCoordinates = new Point3d(p3fData[indices[vertexIndex]].x, p3fData[indices[vertexIndex]].y, p3fData[indices[vertexIndex]].z);
+ }
+ }
+ else {
+ val = indices[vertexIndex] * 3; // for x,y,z
+ closestVertexCoordinates = new Point3d(floatData[val], floatData[val+1], floatData[val+2]);
+ }
+ }
+ else {
+ val = indices[vertexIndex] * 3; // for x,y,z
+ closestVertexCoordinates = new Point3d(doubleData[val], doubleData[val+1], doubleData[val+2]);
+ }
+ }
+ else {
+ float[] floatData = geom.getInterleavedVertices();
+ int offset = getInterleavedVertexOffset(geom);
+ int stride = offset + 3; // for the vertices .
+ val = stride * indices[vertexIndex]+offset;
+ closestVertexCoordinates = new Point3d(floatData[val], floatData[val+1], floatData[val+2]);
+ }
+ }
+ }
+
+ return closestVertexCoordinates;
+ }
+
+
+ /** Get coordinates of closest vertex (world)
+ @return the coordinates of the vertex closest to the intersection point
+ */
+ public Point3d getClosestVertexCoordinatesVW () {
+ if (closestVertexCoordinatesVW == null) {
+ int vertexIndex = getClosestVertexIndex();
+ Point3d[] coordinatesVW = getPrimitiveCoordinatesVW();
+ closestVertexCoordinatesVW = coordinatesVW[vertexIndex];
+ }
+ return closestVertexCoordinatesVW;
+ }
+
+ /** Get index of closest vertex
+ @return the index of the closest vertex
+ */
+ public int getClosestVertexIndex () {
+ if (closestVertexIndex == -1) {
+ storeClosestVertex();
+ }
+ return closestVertexIndex;
+ }
+
+ /** Calculates and stores the closest vertex information */
+ void storeClosestVertex () {
+ if (closestVertexIndex == -1) {
+ double maxDist = Double.MAX_VALUE;
+ double curDist = Double.MAX_VALUE;
+ int closestIndex = -1;
+ /* System.out.println("primitiveCoordinatesVW.length " +
+ primitiveCoordinatesVW.length);
+ */
+ for (int i=0;i<primitiveCoordinatesVW.length;i++) {
+ curDist = pointCoordinatesVW.distance (primitiveCoordinatesVW[i]);
+ /* System.out.println("pointCoordinatesVW " + pointCoordinatesVW);
+ System.out.println("primitiveCoordinatesVW[" + i + "] " +
+ primitiveCoordinatesVW[i]);
+ System.out.println("curDist " + curDist);
+ */
+ if (curDist < maxDist) {
+ closestIndex = i;
+ maxDist = curDist;
+ }
+ }
+ closestVertexIndex = closestIndex;
+ }
+ }
+
+ /* ================================================================== */
+ /* Derived Data: Primitive */
+ /* ================================================================== */
+
+ /**
+ Get the coordinates indices for the intersected primitive. For a non-indexed
+ primitive, this will be the same as the primitive vertex indices
+ @return an array indices
+ */
+ public int[] getPrimitiveCoordinateIndices () {
+
+ if (primitiveCoordinateIndices == null) {
+ if (geometryIsIndexed()) {
+ primitiveCoordinateIndices =
+ new int[primitiveVertexIndices.length];
+ for (int i = 0; i < primitiveVertexIndices.length; i++) {
+ primitiveCoordinateIndices[i] =
+ iGeom.getCoordinateIndex(primitiveVertexIndices[i]);
+ }
+ } else {
+ primitiveCoordinateIndices = primitiveVertexIndices;
+ }
+ }
+ return primitiveCoordinateIndices;
+ }
+
+ /**
+ Get the local coordinates intersected primitive
+ @return an array of Point3d's for the primitive that was intersected
+ */
+ public Point3d[] getPrimitiveCoordinates () {
+ if (primitiveCoordinates == null) {
+ primitiveCoordinates = new Point3d[primitiveVertexIndices.length];
+ int[] indices = getPrimitiveCoordinateIndices();
+ int vformat = geom.getVertexFormat();
+ int val;
+
+ // System.out.println("PickIntersection : indices.length - " + indices.length);
+ if ((vformat & GeometryArray.BY_REFERENCE) == 0) {
+ for (int i = 0; i < indices.length; i++) {
+ primitiveCoordinates[i] = new Point3d();
+ // System.out.println("PickIntersection : indices["+i+"] = " + indices[i]);
+ geom.getCoordinate(indices[i], primitiveCoordinates[i]);
+ }
+ }
+ else {
+ if ((vformat & GeometryArray.INTERLEAVED) == 0) {
+ double[] doubleData = geom.getCoordRefDouble();
+ // If data was set as float then ..
+ if (doubleData == null) {
+ float[] floatData = geom.getCoordRefFloat();
+ if (floatData == null) {
+ Point3f[] p3fData = geom.getCoordRef3f();
+ if (p3fData == null) {
+ Point3d[] p3dData = geom.getCoordRef3d();
+ for (int i = 0; i < indices.length; i++) {
+ primitiveCoordinates[i] = new Point3d(p3dData[indices[i]].x, p3dData[indices[i]].y, p3dData[indices[i]].z);
+ }
+ }
+ else {
+ for (int i = 0; i < indices.length; i++) {
+ primitiveCoordinates[i] = new Point3d(p3fData[indices[i]].x, p3fData[indices[i]].y, p3fData[indices[i]].z);
+ }
+ }
+
+ }
+ else {
+ for (int i = 0; i < indices.length; i++) {
+ val = indices[i] * 3;
+ primitiveCoordinates[i] = new Point3d(floatData[val], floatData[val+1], floatData[val+2]);
+ }
+ }
+ }
+ else {
+ for (int i = 0; i < indices.length; i++) {
+ val = indices[i] * 3;
+ primitiveCoordinates[i] = new Point3d(doubleData[val], doubleData[val+1], doubleData[val+2]);
+ }
+ }
+ }
+ else {
+ float[] floatData = geom.getInterleavedVertices();
+ int offset = getInterleavedVertexOffset(geom);
+ int stride = offset + 3; // for the vertices .
+ for (int i = 0; i < indices.length; i++) {
+ val = stride * indices[i]+offset;
+ primitiveCoordinates[i] = new Point3d(floatData[val], floatData[val+1], floatData[val+2]);
+ }
+ }
+ }
+
+ }
+ return primitiveCoordinates;
+ }
+
+ int getInterleavedVertexOffset(GeometryArray geo) {
+ int offset = 0;
+ int vformat = geo.getVertexFormat();
+ if ((vformat & GeometryArray.COLOR_3) == GeometryArray.COLOR_3) {
+ offset += 3;
+ } else if ((vformat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4){
+ offset += 4;
+ }
+ if ((vformat & GeometryArray.NORMALS) != 0)
+ offset += 3;
+ if ((vformat & GeometryArray.TEXTURE_COORDINATE_2) == GeometryArray.TEXTURE_COORDINATE_2) {
+ offset += 2 * geo.getTexCoordSetCount();
+ }
+ else if ((vformat & GeometryArray.TEXTURE_COORDINATE_3) == GeometryArray.TEXTURE_COORDINATE_3) {
+ offset += 3 * geo.getTexCoordSetCount();
+ }
+
+ return offset;
+ }
+
+ int getInterleavedStride(GeometryArray geo) {
+ int offset = 3; // Add 3 for vertices
+ int vformat = geo.getVertexFormat();
+ if ((vformat & GeometryArray.COLOR_3) == GeometryArray.COLOR_3) {
+ offset += 3;
+ } else if ((vformat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4){
+ offset += 4;
+ }
+ if ((vformat & GeometryArray.NORMALS) != 0)
+ offset += 3;
+ if ((vformat & GeometryArray.TEXTURE_COORDINATE_2) == GeometryArray.TEXTURE_COORDINATE_2) {
+ offset += 2 * geo.getTexCoordSetCount();
+ }
+ else if ((vformat & GeometryArray.TEXTURE_COORDINATE_3) == GeometryArray.TEXTURE_COORDINATE_3) {
+ offset += 3 * geo.getTexCoordSetCount();
+ }
+
+ return offset;
+ }
+
+
+ int getInterleavedColorOffset(GeometryArray geo) {
+ int offset = 0;
+ int vformat = geo.getVertexFormat();
+ if ((vformat & GeometryArray.TEXTURE_COORDINATE_2) == GeometryArray.TEXTURE_COORDINATE_2) {
+ offset += 2 * geo.getTexCoordSetCount();
+ }
+ else if ((vformat & GeometryArray.TEXTURE_COORDINATE_3) == GeometryArray.TEXTURE_COORDINATE_3) {
+ offset += 3 * geo.getTexCoordSetCount();
+ }
+
+ return offset;
+ }
+
+
+ int getInterleavedNormalOffset(GeometryArray geo) {
+ int offset = 0;
+ int vformat = geo.getVertexFormat();
+ if ((vformat & GeometryArray.TEXTURE_COORDINATE_2) == GeometryArray.TEXTURE_COORDINATE_2) {
+ offset += 2 * geo.getTexCoordSetCount();
+ }
+ else if ((vformat & GeometryArray.TEXTURE_COORDINATE_3) == GeometryArray.TEXTURE_COORDINATE_3) {
+ offset += 3 * geo.getTexCoordSetCount();
+ }
+ if ((vformat & GeometryArray.COLOR_3) == GeometryArray.COLOR_3) {
+ offset += 3;
+ } else if ((vformat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4){
+ offset += 4;
+ }
+ return offset;
+ }
+
+
+
+
+ /**
+ Get the normal indices for the intersected primitive. For a non-indexed
+ primitive, this will be the same as the primitive vertex indices
+ If the geometry array does not contain normals this will return null
+ @return an array indices
+ */
+ public int[] getPrimitiveNormalIndices () {
+ if (hasNormals && (primitiveNormalIndices == null)) {
+ if (geometryIsIndexed()) {
+ primitiveNormalIndices =
+ new int[primitiveVertexIndices.length];
+ for (int i = 0; i < primitiveVertexIndices.length; i++) {
+ primitiveNormalIndices[i] =
+ iGeom.getNormalIndex(primitiveVertexIndices[i]);
+ }
+ } else {
+ primitiveNormalIndices = primitiveVertexIndices;
+ }
+ }
+ return primitiveNormalIndices;
+ }
+
+ /**
+ Get the normals of the intersected primitive. This will return null if
+ the primitive does not contain normals.
+ @return an array of Point3d's for the primitive that was intersected
+ */
+ public Vector3f[] getPrimitiveNormals () {
+ if (hasNormals && (primitiveNormals == null)) {
+ primitiveNormals = new Vector3f[primitiveVertexIndices.length];
+ int[] indices = getPrimitiveNormalIndices();
+ int vformat = geom.getVertexFormat();
+ int val;
+
+ if ((vformat & GeometryArray.BY_REFERENCE) == 0) {
+ for (int i = 0; i < indices.length; i++) {
+ primitiveNormals[i] = new Vector3f();
+ geom.getNormal(indices[i], primitiveNormals[i]);
+ }
+ }
+ else {
+ if ((vformat & GeometryArray.INTERLEAVED) == 0) {
+ float[] floatNormals = geom.getNormalRefFloat();
+ if (floatNormals != null) {
+ for (int i = 0; i < indices.length; i++) {
+ val = indices[i] * 3;
+ primitiveNormals[i] = new Vector3f(floatNormals[val],floatNormals[val+1],floatNormals[val+2]);
+ }
+ }
+ else {
+ Vector3f[] normal3f = geom.getNormalRef3f();
+ for (int i = 0; i < indices.length; i++) {
+ primitiveNormals[i] = new Vector3f(normal3f[indices[i]].x,normal3f[indices[i]].y,normal3f[indices[i]].z);
+ }
+ }
+ }
+ else {
+ float[] floatData = geom.getInterleavedVertices();
+ int offset = getInterleavedColorOffset(geom);
+ int stride = getInterleavedStride(geom);
+ for (int i = 0; i < indices.length; i++) {
+ val = stride * indices[i]+offset;
+ primitiveNormals[i] = new Vector3f(floatData[val],floatData[val+1],floatData[val+2]);
+
+ }
+ }
+ }
+ }
+ return primitiveNormals;
+ }
+
+ /**
+ Get the color indices for the intersected primitive. For a non-indexed
+ primitive, this will be the same as the primitive vertex indices
+ If the geometry array does not contain colors this will return null.
+ @return an array indices
+ */
+ public int[] getPrimitiveColorIndices () {
+ if (hasColors && (primitiveColorIndices == null)) {
+ if (geometryIsIndexed()) {
+ primitiveColorIndices =
+ new int[primitiveVertexIndices.length];
+ for (int i = 0; i < primitiveVertexIndices.length; i++) {
+ primitiveColorIndices[i] =
+ iGeom.getColorIndex(primitiveVertexIndices[i]);
+ }
+ } else {
+ primitiveColorIndices = primitiveVertexIndices;
+ }
+ }
+ return primitiveColorIndices;
+ }
+
+ /**
+ Get the colors of the intersected primitive. This will return null if
+ the primitive does not contain colors. If the geometry was defined
+ using GeometryArray.COLOR_3, the 'w' value of the color will be set to 1.0.
+ @return an array of Point3d's for the primitive that was intersected
+ */
+ public Color4f[] getPrimitiveColors () {
+ if (hasColors && (primitiveColors == null)) {
+ primitiveColors = new Color4f[primitiveVertexIndices.length];
+ int[] indices = getPrimitiveColorIndices();
+ int vformat = geom.getVertexFormat();
+ if ((vformat & GeometryArray.BY_REFERENCE) == 0) {
+ if ((vformat & GeometryArray.COLOR_4) ==
+ GeometryArray.COLOR_4) {
+ for (int i = 0; i < indices.length; i++) {
+ primitiveColors[i] = new Color4f();
+ geom.getColor(indices[i], primitiveColors[i]);
+ }
+ } else {
+ Color3f color = new Color3f();
+ for (int i = 0; i < indices.length; i++) {
+ primitiveColors[i] = new Color4f();
+ geom.getColor(indices[i], color);
+ primitiveColors[i].x = color.x;
+ primitiveColors[i].y = color.y;
+ primitiveColors[i].z = color.z;
+ primitiveColors[i].w = 1.0f;
+ }
+ }
+ }
+ else {
+ if ((vformat & GeometryArray.INTERLEAVED) == 0) {
+ float[] floatData = geom.getColorRefFloat();
+ // If data was set as float then ..
+ if (floatData == null) {
+ byte[] byteData = geom.getColorRefByte();
+ if (byteData == null) {
+ Color3f[] c3fData = geom.getColorRef3f();
+ if (c3fData == null) {
+ Color4f[] c4fData = geom.getColorRef4f();
+ if (c4fData == null) {
+ Color3b[] c3bData = geom.getColorRef3b();
+ if (c3bData == null) {
+ Color4b[] c4bData = geom.getColorRef4b();
+ for (int i = 0; i < indices.length; i++) {
+ primitiveColors[i] = new Color4f(c4bData[indices[i]].x,c4bData[indices[i]].y,c4bData[indices[i]].z,c4bData[indices[i]].w);
+
+ }
+ }
+ else {
+ for (int i = 0; i < indices.length; i++) {
+ primitiveColors[i] = new Color4f(c3bData[indices[i]].x,c3bData[indices[i]].y,c3bData[indices[i]].z,1.0f);
+
+ }
+ }
+ }
+ else {
+ for (int i = 0; i < indices.length; i++) {
+ primitiveColors[i] = new Color4f(c4fData[indices[i]].x,c4fData[indices[i]].y,c4fData[indices[i]].z,c4fData[indices[i]].w);
+ }
+ }
+ }
+ else {
+ for (int i = 0; i < indices.length; i++) {
+ primitiveColors[i] = new Color4f(c3fData[indices[i]].x,c3fData[indices[i]].y,c3fData[indices[i]].z,1.0f);
+
+ }
+ }
+ }
+ else {
+ // Could be color3 or color4
+ int val;
+ if ((vformat & GeometryArray.COLOR_4) ==
+ GeometryArray.COLOR_4) {
+ for (int i = 0; i < indices.length; i++) {
+ val = indices[i] << 2; // for color4f
+ primitiveColors[i] = new Color4f(byteData[val],byteData[val+1],byteData[val+2],byteData[val+3]);
+
+ }
+ }
+ else {
+ for (int i = 0; i < indices.length; i++) {
+ val = indices[i] * 3; // for color3f
+ primitiveColors[i] = new Color4f(byteData[val],byteData[val+1],byteData[val+2], 1.0f);
+
+ }
+ }
+ }
+ }
+ else {
+ // Could be color3 or color4
+ int val;
+ if ((vformat & GeometryArray.COLOR_4) ==
+ GeometryArray.COLOR_4) {
+ for (int i = 0; i < indices.length; i++) {
+ val = indices[i] << 2; // for color4f
+ primitiveColors[i] = new Color4f(floatData[val],floatData[val+1],floatData[val+2],floatData[val+3]);
+ }
+ }
+ else {
+ for (int i = 0; i < indices.length; i++) {
+ val = indices[i] * 3; // for color3f
+ primitiveColors[i] = new Color4f(floatData[val],floatData[val+1],floatData[val+2],1.0f);
+
+ }
+ }
+ }
+
+ }
+ else {
+ float[] floatData = geom.getInterleavedVertices();
+ int offset = getInterleavedColorOffset(geom);
+ int stride = getInterleavedStride(geom);
+ for (int i = 0; i < indices.length; i++) {
+ int val = stride * indices[i]+offset;
+ if ((vformat & GeometryArray.COLOR_4) ==
+ GeometryArray.COLOR_4) {
+ primitiveColors[i] = new Color4f(floatData[val],floatData[val+1],floatData[val+2],floatData[val+3]);
+ }
+ else {
+ primitiveColors[i] = new Color4f(floatData[val],floatData[val+1],floatData[val+2],1.0f);
+ }
+ }
+ }
+ }
+ }
+ return primitiveColors;
+ }
+
+ /**
+ Get the texture coordinate indices for the intersected primitive at the specifed
+ index in the specified texture coordinate set. For a non-indexed
+ primitive, this will be the same as the primitive vertex indices
+ If the geometry array does not contain texture coordinates, this will
+ return null.
+ @return an array indices
+ */
+ public int[] getPrimitiveTexCoordIndices (int index) {
+ if (hasTexCoords && (primitiveTexCoordIndices == null)) {
+ if (geometryIsIndexed()) {
+ primitiveTexCoordIndices =
+ new int[primitiveVertexIndices.length];
+ for (int i = 0; i < primitiveVertexIndices.length; i++) {
+ primitiveTexCoordIndices[i] =
+ iGeom.getTextureCoordinateIndex(index,
+ primitiveVertexIndices[i]);
+ }
+ } else {
+ primitiveTexCoordIndices = primitiveVertexIndices;
+ }
+ }
+ return primitiveTexCoordIndices;
+ }
+
+ /**
+ Get the texture coordinates of the intersected primitive at the specifed
+ index in the specified texture coordinate set.
+ null if the primitive does not contain texture coordinates.
+ If the geometry was defined
+ using GeometryArray.TEXTURE_COORDINATE_2, the 'z' value of the texture
+ coordinate will be set to 0.0.
+ @return an array of TexCoord3f's for the primitive that was intersected
+ */
+ public TexCoord3f[] getPrimitiveTexCoords (int index) {
+ if (primitiveTexCoords == null) {
+ primitiveTexCoords = new TexCoord3f[primitiveVertexIndices.length];
+ int[] indices = getPrimitiveTexCoordIndices(index);
+ int vformat = geom.getVertexFormat();
+ if ((vformat & GeometryArray.BY_REFERENCE) == 0) {
+ for (int i = 0; i < indices.length; i++) {
+ primitiveTexCoords[i] = new TexCoord3f();
+ geom.getTextureCoordinate(index, indices[i], primitiveTexCoords[i]);
+ }
+ }
+ else {
+ if ((vformat & GeometryArray.INTERLEAVED) == 0) {
+ int val;
+ float[] floatTexCoords = geom.getTexCoordRefFloat(index);
+ if (floatTexCoords != null) {
+ if ((vformat & GeometryArray.TEXTURE_COORDINATE_2) == GeometryArray.TEXTURE_COORDINATE_2) {
+ for (int i = 0; i < indices.length; i++) {
+ val = indices[i] << 1; // t2f
+ primitiveTexCoords[i] = new TexCoord3f(floatTexCoords[val],
+ floatTexCoords[val+1],
+ 0.0f);
+ }
+ }
+ else {
+ for (int i = 0; i < indices.length; i++) {
+ val = indices[i] * 3; // t3f
+ primitiveTexCoords[i] = new TexCoord3f(floatTexCoords[val],
+ floatTexCoords[val+1],
+ floatTexCoords[val+2]);
+ }
+ }
+ }
+ else {
+ TexCoord2f[] texCoord2f = geom.getTexCoordRef2f(index);
+ if (texCoord2f != null) {
+ for (int i = 0; i < indices.length; i++) {
+ primitiveTexCoords[i] = new TexCoord3f(texCoord2f[indices[i]].x,
+ texCoord2f[indices[i]].y,
+ 0.0f);
+ }
+ }
+ else {
+ TexCoord3f[] texCoord3f = geom.getTexCoordRef3f(index);
+ for (int i = 0; i < indices.length; i++) {
+ primitiveTexCoords[i] = new TexCoord3f(texCoord3f[indices[i]].x,
+ texCoord3f[indices[i]].y,
+ texCoord3f[indices[i]].z);
+ }
+ }
+
+ }
+ }
+ else {
+ float[] floatData = geom.getInterleavedVertices();
+ int stride = getInterleavedStride(geom);
+ int offset;
+ // Get the correct tex coord set
+ if ((vformat & GeometryArray.TEXTURE_COORDINATE_2) ==
+ GeometryArray.TEXTURE_COORDINATE_2) {
+ offset = index << 1;
+ }
+ else {
+ offset = index * 3;
+ }
+ for (int i = 0; i < indices.length; i++) {
+ int val = stride * indices[i];
+ if ((vformat & GeometryArray.TEXTURE_COORDINATE_2) ==
+ GeometryArray.TEXTURE_COORDINATE_2) {
+ primitiveTexCoords[i] = new TexCoord3f(floatData[val +offset ],floatData[val+1+offset],0.0f);
+ }
+ else {
+ primitiveTexCoords[i] = new TexCoord3f(floatData[val+offset],floatData[val+1+offset],floatData[val+2+offset]);
+ }
+ }
+ }
+ }
+ }
+ return primitiveTexCoords;
+ }
+
+
+
+
+ /* ================================================================== */
+ /* Derived Data: Intersection Point */
+ /* ================================================================== */
+
+ /**
+ Returns the coordinates of the intersection point (local coordinates),
+ if available.
+ @return coordinates of the intersection point
+ */
+ public Point3d getPointCoordinates() {
+ if (pointCoordinates == null) {
+ double[] weights = getInterpWeights();
+ Point3d[] coords = getPrimitiveCoordinates();
+ pointCoordinates = new Point3d();
+ for (int i = 0; i < weights.length; i++) {
+ pointCoordinates.x += weights[i] * coords[i].x;
+ pointCoordinates.y += weights[i] * coords[i].y;
+ pointCoordinates.z += weights[i] * coords[i].z;
+ }
+ }
+ return pointCoordinates;
+ }
+
+ /**
+ Returns the normal of the intersection point. Returns null if the geometry
+ does not contain normals.
+ @return normal at the intersection point.
+ */
+ public Vector3f getPointNormal() {
+ if (hasNormals && (pointNormal == null)) {
+ double[] weights = getInterpWeights();
+ Vector3f[] normals = getPrimitiveNormals();
+ pointNormal = new Vector3f();
+ for (int i = 0; i < weights.length; i++) {
+ pointNormal.x += (float) weights[i] * normals[i].x;
+ pointNormal.y += (float) weights[i] * normals[i].y;
+ pointNormal.z += (float) weights[i] * normals[i].z;
+ }
+ }
+ return pointNormal;
+ }
+
+ /**
+ Returns the color of the intersection point. Returns null if the geometry
+ does not contain colors. If the geometry was defined with
+ GeometryArray.COLOR_3, the 'w' component of the color will initialized to
+ 1.0
+ @return color at the intersection point.
+ */
+ public Color4f getPointColor() {
+ if (hasColors && (pointColor == null)) {
+ double[] weights = getInterpWeights();
+ Color4f[] colors = getPrimitiveColors();
+ pointColor = new Color4f();
+ for (int i = 0; i < weights.length; i++) {
+ pointColor.x += (float) weights[i] * colors[i].x;
+ pointColor.y += (float) weights[i] * colors[i].y;
+ pointColor.z += (float) weights[i] * colors[i].z;
+ pointColor.w += (float) weights[i] * colors[i].w;
+ }
+ }
+ return pointColor;
+ }
+
+ /**
+ Returns the texture coordinate of the intersection point at the specifed
+ index in the specified texture coordinate set.
+ Returns null if the geometry
+ does not contain texture coordinates. If the geometry was defined with
+ GeometryArray.TEXTURE_COORDINATE_3, the 'z' component of the texture
+ coordinate will initialized to 0.0
+ @return texture coordinate at the intersection point.
+ */
+ public TexCoord3f getPointTextureCoordinate(int index) {
+ if (hasTexCoords && (pointTexCoord == null)) {
+ double[] weights = getInterpWeights();
+ TexCoord3f[] texCoords = getPrimitiveTexCoords(index);
+ pointTexCoord = new TexCoord3f();
+ for (int i = 0; i < weights.length; i++) {
+ pointTexCoord.x += (float) weights[i] * texCoords[i].x;
+ pointTexCoord.y += (float) weights[i] * texCoords[i].y;
+ pointTexCoord.z += (float) weights[i] * texCoords[i].z;
+ }
+ }
+ return pointTexCoord;
+ }
+
+ /* ================================================================== */
+ /* Utility code for interpolating intersection point data */
+ /* ================================================================== */
+
+ /* absolute value */
+ double
+ abs(double value) {
+ if (value < 0.0) {
+ return -value;
+ } else {
+ return value;
+ }
+ }
+
+
+ /* return the axis corresponding to the largest component of delta */
+ int
+ maxAxis(Vector3d delta) {
+ int axis = X_AXIS;
+ double max = abs(delta.x);
+ if (abs(delta.y) > max) {
+ axis = Y_AXIS;
+ max = abs(delta.y);
+ }
+ if (abs(delta.z) > max) {
+ axis = Z_AXIS;
+ }
+ return axis;
+ }
+
+ /* Triangle interpolation. Basic idea:
+ * Map the verticies of the triangle to the form:
+ *
+ * L--------R
+ * \ /
+ * IL+--P-+IR
+ * \ /
+ * Base
+ *
+ where P is the intersection point Base, L and R and the triangle
+ points. IL and IR are the projections if P along the Base-L and Base-R
+ edges using an axis:
+
+ IL = leftFactor * L + (1- leftFactor) * Base
+ IR = rightFactor * R + (1-rightFactor) * Base
+
+ then find the interp factor, midFactor, for P between IL and IR. If
+ this is outside the range 0->1 then we have the wrong triangle of a
+ quad and we return false.
+
+ Else, the weighting is:
+
+ IP = midFactor * IL + (1 - midFactor) * IR;
+
+ Solving for weights for the formula:
+ IP = BaseWeight * Base + LeftWeight * L + RightWeight * R;
+ We get:
+ BaseWeight = 1 - midFactor * leftFactor
+ - rightFactor + midFactor * rightFactor;
+ LeftWeight = midFactor * leftFactor;
+ RightWeight = righFactor - midFactor * rightFactor;
+ As a check, note that the sum of the weights is 1.0.
+ */
+
+ boolean
+ interpTriangle(int index0, int index1, int index2, Point3d[] coords,
+ Point3d intPt) {
+
+ // find the longest edge, we'll use that to pick the axis */
+ Vector3d delta0 = new Vector3d();
+ Vector3d delta1 = new Vector3d();
+ Vector3d delta2 = new Vector3d();
+ delta0.sub(coords[index1], coords[index0]);
+ delta1.sub(coords[index2], coords[index0]);
+ delta2.sub(coords[index2], coords[index1]);
+ double len0 = delta0.lengthSquared();
+ double len1 = delta1.lengthSquared();
+ double len2 = delta2.lengthSquared();
+ Vector3d longest = delta0;
+ double maxLen = len0;
+ if (len1 > maxLen) {
+ longest = delta1;
+ maxLen = len1;
+ }
+ if (len2 > maxLen) {
+ longest = delta2;
+ }
+ int mainAxis = maxAxis(longest);
+
+ /*
+ System.out.println("index0 = " + index0 + " index1 = " + index1 +
+ " index2 = " + index2);
+
+ System.out.println("coords[index0] = " + coords[index0]);
+ System.out.println("coords[index1] = " + coords[index1]);
+ System.out.println("coords[index2] = " + coords[index2]);
+ System.out.println("intPt = " + intPt);
+
+ System.out.println("delta0 = " + delta0 + " len0 " + len0);
+ System.out.println("delta1 = " + delta1 + " len1 " + len1);
+ System.out.println("delta2 = " + delta2 + " len2 " + len2);
+ */
+
+ /* now project the intersection point along the axis onto the edges */
+ double[] factor = new double[3];
+ /* the factor is for the projection opposide the vertex 0 = 1->2, etc*/
+ factor[0] =
+ getInterpFactorForBase(intPt, coords[index1], coords[index2], mainAxis);
+ factor[1] =
+ getInterpFactorForBase(intPt, coords[index2], coords[index0], mainAxis);
+ factor[2] =
+ getInterpFactorForBase(intPt, coords[index0], coords[index1], mainAxis);
+
+ if (debug) {
+ System.out.println("intPt = " + intPt);
+ switch(mainAxis) {
+ case X_AXIS:
+ System.out.println("mainAxis = X_AXIS");
+ break;
+ case Y_AXIS:
+ System.out.println("mainAxis = Y_AXIS");
+ break;
+ case Z_AXIS:
+ System.out.println("mainAxis = Z_AXIS");
+ break;
+ }
+ System.out.println("factor[0] = " + factor[0]);
+ System.out.println("factor[1] = " + factor[1]);
+ System.out.println("factor[2] = " + factor[2]);
+ }
+
+ /* Find the factor that is out of range, it will tell us which
+ * vertex to use for base
+ */
+ int base, left, right;
+ double leftFactor, rightFactor;
+ if ((factor[0] < 0.0) || (factor[0] > 1.0)) {
+ base = index0;
+ right = index1;
+ left = index2;
+ rightFactor = factor[2];
+ leftFactor = 1.0 - factor[1];
+ if (debug) {
+ System.out.println("base 0, rightFactor = " + rightFactor +
+ " leftFactor = " + leftFactor);
+ }
+ } else if ((factor[1] < 0.0) || (factor[1] > 1.0)) {
+ base = index1;
+ right = index2;
+ left = index0;
+ rightFactor = factor[0];
+ leftFactor = 1.0 - factor[2];
+ if (debug) {
+ System.out.println("base 1, rightFactor = " + rightFactor +
+ " leftFactor = " + leftFactor);
+ }
+ } else {
+ base = index2;
+ right = index0;
+ left = index1;
+ rightFactor = factor[1];
+ leftFactor = 1.0 - factor[0];
+ if (debug) {
+ System.out.println("base 2, rightFactor = " + rightFactor +
+ " leftFactor = " + leftFactor);
+ }
+ }
+ if (debug) {
+ System.out.println("base = " + coords[base]);
+ System.out.println("left = " + coords[left]);
+ System.out.println("right = " + coords[right]);
+ }
+ /* find iLeft and iRight */
+ Point3d iLeft = new Point3d(leftFactor * coords[left].x +
+ (1.0-leftFactor)*coords[base].x,
+ leftFactor * coords[left].y +
+ (1.0-leftFactor)*coords[base].y,
+ leftFactor * coords[left].z +
+ (1.0-leftFactor)*coords[base].z);
+
+ Point3d iRight = new Point3d(rightFactor * coords[right].x +
+ (1.0-rightFactor)*coords[base].x,
+ rightFactor * coords[right].y +
+ (1.0-rightFactor)*coords[base].y,
+ rightFactor * coords[right].z +
+ (1.0-rightFactor)*coords[base].z);
+
+ if (debug) {
+ System.out.println("iLeft = " + iLeft);
+ System.out.println("iRight = " + iRight);
+ }
+
+ /* now find an axis and solve for midFactor */
+ delta0.sub(iLeft, iRight);
+ int midAxis = maxAxis(delta0);
+ double midFactor = getInterpFactor(intPt, iRight, iLeft, midAxis);
+
+ if (debug) {
+ switch(midAxis) {
+ case X_AXIS:
+ System.out.println("midAxis = X_AXIS");
+ break;
+ case Y_AXIS:
+ System.out.println("midAxis = Y_AXIS");
+ break;
+ case Z_AXIS:
+ System.out.println("midAxis = Z_AXIS");
+ break;
+ }
+ System.out.println("midFactor = " + midFactor);
+ }
+
+ if (midFactor < 0.0) {
+ // System.out.println("midFactor = " + midFactor);
+ if ((midFactor + TOL) >= 0.0) {
+ // System.out.println("In Tol case : midFactor = " + midFactor);
+ midFactor = 0.0;
+ }
+ else {
+ /* int point is outside triangle */
+ return false;
+ }
+ }
+ else if (midFactor > 1.0) {
+ // System.out.println("midFactor = " + midFactor);
+ if ((midFactor-TOL) <= 1.0) {
+ // System.out.println("In Tol case : midFactor = " + midFactor);
+ midFactor = 1.0;
+ }
+ else {
+ /* int point is outside triangle */
+ return false;
+ }
+ }
+
+ // Assign the weights
+ interpWeights[base] = 1.0 - midFactor * leftFactor -
+ rightFactor + midFactor * rightFactor;
+ interpWeights[left] = midFactor * leftFactor;
+ interpWeights[right] = rightFactor - midFactor * rightFactor;
+ return true;
+
+ }
+
+ /* Get the interpolation weights for each of the verticies of the
+ * primitive.
+ */
+ double[] getInterpWeights() {
+
+ Point3d pt = getPointCoordinatesVW();
+ Point3d[] coordinates = getPrimitiveCoordinatesVW();
+ double factor;
+ int axis;
+
+ if (interpWeights != null) {
+ return interpWeights;
+ }
+
+ interpWeights = new double[coordinates.length];
+
+ // Interpolate
+ switch (coordinates.length) {
+ case 1:
+ // Nothing to interpolate
+ interpWeights[0] = 1.0;
+ break;
+ case 2: // edge
+ Vector3d delta = new Vector3d();
+ delta.sub (coordinates[1], coordinates[0]);
+ axis = maxAxis(delta);
+ factor = getInterpFactor (pt, coordinates[1], coordinates[0], axis);
+ interpWeights[0] = factor;
+ interpWeights[1] = 1.0 - factor;
+ break;
+ case 3: // triangle
+ if (!interpTriangle(0, 1, 2, coordinates, pt)) {
+ throw new RuntimeException ("Interp point outside triangle");
+ }
+ break;
+ case 4: // quad
+ if (!interpTriangle(0, 1, 2, coordinates, pt)) {
+ if (!interpTriangle(0, 2, 3, coordinates, pt)) {
+ throw new RuntimeException ("Interp point outside quad");
+ }
+ }
+ break;
+ default:
+ throw new RuntimeException ("Unexpected number of points.");
+ }
+ return interpWeights;
+ }
+
+ /**
+ Calculate the interpolation factor for point p by projecting it along
+ an axis (x,y,z) onto the edge between p1 and p2. If the result is
+ in the 0->1 range, point is between p1 and p2 (0 = point is at p1,
+ 1 => point is at p2).
+ */
+ private static float getInterpFactor (Point3d p, Point3d p1, Point3d p2,
+ int axis) {
+ float t;
+ switch (axis) {
+ case X_AXIS:
+ if (p1.x == p2.x)
+ //t = Float.MAX_VALUE; // TODO: should be 0?
+ t = 0.0f;
+ else
+ t = (float) ((p1.x - p.x) / (p1.x - p2.x));
+ break;
+ case Y_AXIS:
+ if (p1.y == p2.y)
+ // t = Float.MAX_VALUE;
+ t = 0.0f;
+ else
+ t = (float) ((p1.y - p.y) / (p1.y - p2.y));
+ break;
+ case Z_AXIS:
+ if (p1.z == p2.z)
+ // t = Float.MAX_VALUE;
+ t = 0.0f;
+ else
+ t = (float)((p1.z - p.z) / (p1.z - p2.z));
+ break;
+ default:
+ throw new RuntimeException ("invalid axis parameter "+axis+" (must be 0-2)");
+ }
+ return t;
+ }
+
+ /**
+ Calculate the interpolation factor for point p by projecting it along
+ an axis (x,y,z) onto the edge between p1 and p2. If the result is
+ in the 0->1 range, point is between p1 and p2 (0 = point is at p1,
+ 1 => point is at p2).
+ return MAX_VALUE if component of vertices are the same.
+ */
+ private static float getInterpFactorForBase (Point3d p, Point3d p1, Point3d p2,
+ int axis) {
+ float t;
+ switch (axis) {
+ case X_AXIS:
+ if (p1.x == p2.x)
+ t = Float.MAX_VALUE;
+ else
+ t = (float) ((p1.x - p.x) / (p1.x - p2.x));
+ break;
+ case Y_AXIS:
+ if (p1.y == p2.y)
+ t = Float.MAX_VALUE;
+ else
+ t = (float) ((p1.y - p.y) / (p1.y - p2.y));
+ break;
+ case Z_AXIS:
+ if (p1.z == p2.z)
+ t = Float.MAX_VALUE;
+ else
+ t = (float)((p1.z - p.z) / (p1.z - p2.z));
+ break;
+ default:
+ throw new RuntimeException ("invalid axis parameter "+axis+" (must be 0-2)");
+ }
+ return t;
+ }
+
+
+} // PickIntersection
+
+
+
+
+
+
+
+
+
diff --git a/src/classes/share/com/sun/j3d/utils/picking/PickResult.java b/src/classes/share/com/sun/j3d/utils/picking/PickResult.java
new file mode 100644
index 0000000..e9f3adf
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/picking/PickResult.java
@@ -0,0 +1,3344 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.picking;
+
+import javax.vecmath.*;
+import javax.media.j3d.*;
+import java.util.ArrayList;
+import com.sun.j3d.utils.geometry.Primitive;
+import com.sun.j3d.internal.*;
+
+/**
+ * Stores information about a pick hit.
+ * Detailed information about the pick and each intersection of the PickShape
+ * with the picked Node can be inquired. The PickResult is constructed with
+ * basic information and more detailed information is generated as needed. The
+ * additional information is only available if capability bits on the scene
+ * graph Nodes are set properly;
+ * <A HREF="PickTool.html#setCapabilities(javax.media.j3d.Node, int)">
+ * <code>PickTool.setCapabilties(Node, int)</code></A>
+ * can
+ * be used to ensure correct capabilites are set. Inquiring data which is not
+ * available due to capabilties not being set will generate a
+ * <code>CapabilityNotSet</code> exception.
+ * <p>
+ * A PickResult can be used to calculate intersections on Node which is not part
+ * of a live scene graph using the constructor which takes a local to VWorld
+ * transformation for the Node.
+ * <p>
+ * Pick hits on TriangleStrip primitives will store the triangle points in the
+ * PickIntersection with
+ * the verticies in counter-clockwise order. For triangles which start with
+ * an odd numbered vertex this will be the the opposite of the
+ * order of the points in the TriangleStrip.
+ * This way the triangle in
+ * the PickIntersection will display the same was as the triangle in the
+ * strip.
+ * <p>
+ * If the Shape3D being picked has multiple geometry arrays, the arrays are
+ * stored in the PickResult and referred to by a geometry index.
+ * <p>
+ * If the Shape3D refers to a CompressedGeometry, the geometry is decompressed
+ * into an array of Shape3D nodes which can be inquired. The geometry
+ * NodeComponents for the Shape3D nodes are stored and used as if the Shape3D
+ * had multiple geometries. If there are multiple CompressedGeometries on the
+ * Shape3D, the decompressed Shape3Ds and GeometryArrays will be stored
+ * sequentially.
+ * <p>
+ * The intersection point for Morph nodes cannot be calculated using the
+ * displayed geometry
+ * due to limitations in the current Java3D core API (the current
+ * geometry of the the Morph cannot be inquired). Instead
+ * the geometry at index 0 in the Morph is used. This limitation may
+ * be eliminated in a future release of Java3D.
+ */
+public class PickResult {
+
+ /* OPEN ISSUES:
+ -- getInterpolatedTextureCoordinates uses the depricated API faor
+ getTextureCoordinate(), need to update.
+ -- Bounds tests don't fill in any picking info.
+ -- Can't do any intersections with the PickPoint shape.
+ */
+
+
+ // Externally used constants
+
+ /**
+ * Flag to pass to
+ * <CODE>getNode(int)</CODE>
+ * to return a
+ * <code>Shape3D</code> node from
+ * the <code>SceneGraphPath</code>.
+ */
+ public static final int SHAPE3D = 0x1;
+
+ /**
+ * Flag to pass to
+ * <CODE>getNode(int)</CODE>
+ * to return a
+ * <code>Morph</code> node from
+ * the <code>SceneGraphPath</code>.
+ */
+ public static final int MORPH = 0x2;
+
+ /**
+ * Flag to pass to
+ * <CODE>getNode(int)</CODE>
+
+ * to return a
+ * <code>Primitive</code> node from
+ * the <code>SceneGraphPath</code>.
+ */
+ public static final int PRIMITIVE = 0x4;
+
+ /**
+ * Flag to pass to
+ * <CODE>getNode(int)</CODE>
+ * to return a
+ * <code>Link</code> node from
+ * the <code>SceneGraphPath</code>.
+ */
+ public static final int LINK = 0x8;
+
+ /**
+ * Flag to pass to
+ * <CODE>getNode(int)</CODE>
+ * to return a
+ * <code>Group</code> node from
+ * the <code>SceneGraphPath</code>.
+ */
+ public static final int GROUP = 0x10;
+
+ /**
+ * Flag to pass to
+ * <CODE>getNode(int)</CODE>
+ * to return a
+ * <code>TransformGroup</code> node from
+ * the <code>SceneGraphPath</code>.
+ */
+ public static final int TRANSFORM_GROUP = 0x20;
+
+ /**
+ * Flag to pass to
+ * <CODE>getNode(int)</CODE>
+ * to return a
+ * <code>BranchGroup</code> node from
+ * the <code>SceneGraphPath</code>.
+ */
+ public static final int BRANCH_GROUP = 0x40;
+
+ /**
+ * Flag to pass to
+ * <CODE>getNode(int)</CODE>
+ * to return a
+ * <code>Switch</code> node from
+ * the <code>SceneGraphPath</code>.
+ */
+ public static final int SWITCH = 0x80;
+
+
+
+ /* =================== ATTRIBUTES ======================= */
+ static boolean debug = false;
+
+ /** if true, find only the first intersection */
+ boolean firstIntersectOnly = false;
+
+ /** Stored SceneGraphPath */
+ SceneGraphPath pickedSceneGraphPath = null;
+
+ /** Picked node: shape3d, text3d, etc. */
+ Node pickedNode = null;
+
+ /** GeometryArray(s) of the picked node */
+ GeometryArray[] geometryArrays = null;
+
+ /** Shape3Ds from CompressedGeometry on the picked node */
+ Shape3D[] compressGeomShape3Ds = null;
+
+ /** Transform to World Coordinates */
+ Transform3D localToVWorld = null;
+
+ /** the pick shape to use for intersections */
+ PickShape pickShape = null;
+ /* data derived from the pick shape */
+ int pickShapeType = -1;
+ Vector3d pickShapeDir = null;
+ Point3d pickShapeStart = null;
+ Point3d pickShapeEnd = null;
+ Bounds pickShapeBounds = null;
+
+ static final Point3d zeroPnt = new Point3d();
+
+ /** ArrayList to store intersection results */
+ ArrayList intersections = null;
+
+ // internal constants used for intersections
+ static final double FUZZ = 1E-6; /* fuzziness factor used to determine
+ if two lines are parallel */
+ static final int PICK_SHAPE_RAY = 1;
+ static final int PICK_SHAPE_SEGMENT = 2;
+ static final int PICK_SHAPE_POINT = 3;
+ static final int PICK_SHAPE_BOUNDING_BOX = 4;
+ static final int PICK_SHAPE_BOUNDING_SPHERE = 5;
+ static final int PICK_SHAPE_BOUNDING_POLYTOPE = 6;
+ static final int PICK_SHAPE_CYLINDER = 7;
+ static final int PICK_SHAPE_CONE = 8;
+
+ static final double EPS = 1.0e-13;
+
+
+ /* =================== METHODS ======================= */
+
+ /** Default constructor. */
+ PickResult () { }
+
+ /** Construct a PickResult using a SceneGraphPath
+ @param sgp SceneGraphPath associated with this PickResult
+ @param ps The pickShape to intersect against
+ */
+ public PickResult (SceneGraphPath sgp, PickShape ps) {
+ pickedSceneGraphPath = sgp;
+ pickedNode = sgp.getObject();
+ localToVWorld = sgp.getTransform();
+ pickShape = ps;
+ initPickShape();
+ }
+
+
+ /** Construct a PickResult using the Node and localToVWorld transform
+ @param pn The picked node.
+ @param l2vw The local to VWorld transformation for the node
+ @param ps The PickShape to intersect against
+ @throws IllegalArgumentException If the node is not a Morph or Shape3D.
+ */
+ public PickResult (Node pn, Transform3D l2vw, PickShape ps) {
+ if ((pn instanceof Shape3D) || (pn instanceof Morph)) {
+ pickedNode = pn;
+ localToVWorld = l2vw;
+ pickShape = ps;
+ initPickShape();
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ // similar to the constructor, resets the data in PickResult so it can be
+ // reused via a freelist
+ void reset(SceneGraphPath sgp, PickShape ps) {
+ firstIntersectOnly = false;
+ geometryArrays = null;
+ compressGeomShape3Ds = null;
+ pickShapeBounds = null;
+ intersections = null;
+ pickedSceneGraphPath = sgp;
+ pickedNode = sgp.getObject();
+ localToVWorld = sgp.getTransform();
+ pickShape = ps;
+ initPickShape();
+ }
+
+ // similar to the constructor, resets the data in PickResult so
+ // it can be reused via a freelist
+ void reset(Node pn, Transform3D l2vw, PickShape ps) {
+ if ((pn instanceof Shape3D) || (pn instanceof Morph)) {
+ firstIntersectOnly = false;
+ geometryArrays = null;
+ compressGeomShape3Ds = null;
+ pickShapeBounds = null;
+ intersections = null;
+ pickedSceneGraphPath = null;
+ pickedNode = pn;
+ localToVWorld = l2vw;
+ pickShape = ps;
+ initPickShape();
+ }
+ else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ void initPickShape() {
+ if(pickShape instanceof PickRay) {
+ if (pickShapeStart == null) pickShapeStart = new Point3d();
+ if (pickShapeDir == null) pickShapeDir = new Vector3d();
+ ((PickRay) pickShape).get (pickShapeStart, pickShapeDir);
+ pickShapeType = PICK_SHAPE_RAY;
+ } else if (pickShape instanceof PickSegment) {
+ if (pickShapeStart == null) pickShapeStart = new Point3d();
+ if (pickShapeEnd == null) pickShapeEnd = new Point3d();
+ if (pickShapeDir == null) pickShapeDir = new Vector3d();
+ ((PickSegment)pickShape).get(pickShapeStart, pickShapeEnd);
+ pickShapeDir.set (pickShapeEnd.x - pickShapeStart.x,
+ pickShapeEnd.y - pickShapeStart.y,
+ pickShapeEnd.z - pickShapeStart.z);
+ pickShapeType = PICK_SHAPE_SEGMENT;
+ } else if (pickShape instanceof PickBounds) {
+ pickShapeBounds = ((PickBounds) pickShape).get();
+ if ( pickShapeBounds instanceof BoundingBox )
+ pickShapeType = PICK_SHAPE_BOUNDING_BOX;
+ else if( pickShapeBounds instanceof BoundingSphere )
+ pickShapeType = PICK_SHAPE_BOUNDING_SPHERE;
+ else if( pickShapeBounds instanceof BoundingPolytope )
+ pickShapeType = PICK_SHAPE_BOUNDING_POLYTOPE;
+ } else if(pickShape instanceof PickPoint) {
+ throw new RuntimeException ("PickPoint doesn't make sense for geometry-based picking. Java 3D doesn't have spatial information of the surface. Should use PickBounds with BoundingSphere and set radius to a epsilon tolerance.");
+ } else if (pickShape instanceof PickCylinder) {
+ pickShapeType = PICK_SHAPE_CYLINDER;
+ } else if (pickShape instanceof PickCone) {
+ pickShapeType = PICK_SHAPE_CONE;
+ } else {
+ throw new
+ RuntimeException("PickShape not supported for intersection");
+ }
+ }
+
+ /** Get the SceneGraphPath. This will be null if the non SceneGraphPath
+ * constructor was used.
+ */
+ public SceneGraphPath getSceneGraphPath() {
+ /* Q: should this return a copy */
+ return pickedSceneGraphPath;
+ }
+
+
+ /** Get the localToVworld transform for the Node
+ */
+ public Transform3D getLocalToVworld() {
+ return localToVWorld;
+ }
+
+ /** Get the GeometryArray at index 0 for the picked node
+ */
+ public GeometryArray getGeometryArray() {
+ if (geometryArrays == null) {
+ storeGeometry();
+ }
+ return geometryArrays[0];
+ }
+
+ /** Get the array of GeometryArrays for the picked node
+ */
+ public GeometryArray[] getGeometryArrays() {
+ if (geometryArrays == null) {
+ storeGeometry();
+ }
+ return geometryArrays;
+ }
+
+ /** Get the number of GeometryArrays for the picked node
+ */
+ public int numGeometryArrays() {
+ if (geometryArrays == null) {
+ storeGeometry();
+ }
+ return geometryArrays.length;
+ }
+
+ /** Get the number of Shape3Ds that came from decompressing a
+ CompressedGeometry on the picked node.
+ */
+ public int numCompressedGeometryShape3Ds() {
+ if (geometryArrays == null) {
+ storeGeometry();
+ }
+ if (compressGeomShape3Ds == null) {
+ return 0;
+ } else {
+ return compressGeomShape3Ds.length;
+ }
+ }
+
+ /** Get the array of Shape3Ds that came from decompressing a
+ CompressedGeometry on the picked node.
+ */
+ public Shape3D[] getCompressedGeometryShape3Ds() {
+ if (geometryArrays == null) {
+ storeGeometry();
+ }
+ if (compressGeomShape3Ds == null) {
+ return null;
+ } else {
+ return compressGeomShape3Ds;
+ }
+ }
+
+ /** Get the PickShape used for intersections
+ */
+ public PickShape getPickShape() {
+ return pickShape;
+ }
+
+ /** Set the PickResult to find only the first intersection of the PickShape
+ * with the Node. The default is <code>false</code> (all intersections are
+ * found)
+ */
+ public void setFirstIntersectOnly(boolean flag) {
+ firstIntersectOnly = flag;
+ }
+
+ /** Return the "first intersection only" value. */
+ public boolean getFirstPickEnable() {
+ return firstIntersectOnly;
+ }
+
+ /** Returns the number of PickIntersections in the PickResult.
+ @return the number of intersections
+ */
+ public int numIntersections () {
+ if (intersections == null) {
+ generateIntersections();
+ }
+ return intersections.size();
+ }
+
+ /** Returns a specific PickIntersection object
+ @param index the index number
+ @return the PickIntersection referenced by the index number
+ */
+ public PickIntersection getIntersection (int index) {
+ if (intersections == null) {
+ generateIntersections();
+ }
+ return (PickIntersection) intersections.get (index);
+ }
+
+ /** Gets the PickIntersection in this PickResult that is closest to a point
+ @param pt the point to use for distance calculations
+ @return the closest PickIntersection object
+ */
+ public PickIntersection getClosestIntersection (Point3d pt) {
+ PickIntersection pi = null;
+ PickIntersection curPi = null;
+ Point3d curPt = null;
+ double minDist = Double.MAX_VALUE;
+ double curDist = 0.0;
+
+ if (pt == null) return null;
+
+ if (intersections == null) {
+ generateIntersections();
+ }
+
+ for (int i=0;i<intersections.size();i++) {
+ if ((null != (curPi = getIntersection(i))) &&
+ (null != (curPt = curPi.getPointCoordinatesVW()))) {
+ curDist = pt.distance (curPt);
+ if (curDist < minDist) {
+ pi = curPi;
+ minDist = curDist;
+ }
+ }
+ }
+ return pi;
+ }
+
+
+ /**
+ Returns String representation
+ @return string representation of this object
+ */
+ public String toString () {
+ String rt = new String ("PickResult: sgp:"+pickedSceneGraphPath+"\n");
+ if (pickedNode != null) rt += " node:"+pickedNode;
+
+
+ // TODO: catch cap not set exceptions and return no intersection info
+ if (intersections == null) {
+ generateIntersections();
+ }
+
+ if (intersections.size() > 0) {
+ for (int i=0;i<intersections.size();i++) {
+ rt +="\n";
+ rt += ((PickIntersection)intersections.get(i)).toString2();
+ }
+ }
+
+ return rt;
+ }
+
+ /** Store the geometry for the node in this PickResult */
+ private void storeGeometry () {
+ if (pickedNode instanceof Morph) {
+ geometryArrays = new GeometryArray[1];
+ geometryArrays[0] =
+ (GeometryArray) ((Morph)pickedNode).getGeometryArray (0);
+ } else if (pickedNode instanceof Shape3D) {
+ Shape3D shape = ((Shape3D)pickedNode);
+ ArrayList geoArrays = new ArrayList();
+ for (int k = 0; k < shape.numGeometries(); k++) {
+ Geometry geometry = shape.getGeometry(k);
+ if (geometry instanceof CompressedGeometry) {
+ Shape3D[] sa = ((CompressedGeometry)geometry).decompress();
+ // System.out.println ("Decompressed geometry has "+sa.length+
+ // " Shape3Ds");
+ if (sa != null) {
+ for (int j = 0; j < sa.length; j++) {
+ for (int i = 0; i < sa[j].numGeometries(); i++) {
+ geoArrays.add(sa[j].getGeometry(i));
+ }
+ }
+ }
+ if (compressGeomShape3Ds == null) {
+ // just save the new one
+ compressGeomShape3Ds = sa;
+ } else {
+ // append the the new one on the end of the old array
+ Shape3D[] save = compressGeomShape3Ds;
+ int newLength = save.length + sa.length;
+ compressGeomShape3Ds = new Shape3D[newLength];
+ System.arraycopy(save, 0, compressGeomShape3Ds, 0,
+ save.length);
+ System.arraycopy(sa, 0, compressGeomShape3Ds, save.length,
+ sa.length);
+ }
+ } else if (geometry instanceof GeometryArray) {
+ geoArrays.add(geometry);
+ }
+ }
+ geometryArrays = new GeometryArray[geoArrays.size()];
+ for (int i = 0; i < geoArrays.size(); i++) {
+ geometryArrays[i] = (GeometryArray) geoArrays.get(i);
+ }
+ }
+ if (geometryArrays == null) {
+ if (pickedNode instanceof Shape3D) {
+ Shape3D shape = (Shape3D) pickedNode;
+ }
+ throw new RuntimeException ("Type of the picked node is not supported");
+ }
+ }
+
+ /** Get the picked node */
+ public Node getObject () {
+ // get node from scenegraphpath
+ if (pickedNode == null) {
+ storeNode ();
+ }
+ return pickedNode;
+ }
+
+ /** Set the picked node */
+ void setObject (Node n) {
+ pickedNode = n;
+ }
+
+ /** Get the first node of a certain type up the SceneGraphPath
+ @param flags the type of node we are interested in
+ @return a Node object
+ */
+ public Node getNode (int flags) {
+ if (pickedNode == null) {
+ storeNode ();
+ }
+ if ((pickedNode instanceof Shape3D) && ((flags & SHAPE3D) != 0)){
+ if (debug) System.out.println("Shape3D found");
+ return pickedNode;
+ }
+ else if ((pickedNode instanceof Morph) && ((flags & MORPH) != 0)){
+ if (debug) System.out.println("Morph found");
+ return pickedNode;
+ }
+ else {
+ if (pickedSceneGraphPath == null) {
+ return null;
+ }
+ for (int j=pickedSceneGraphPath.nodeCount()-1; j>=0; j--){
+ Node pNode = pickedSceneGraphPath.getNode(j);
+ if (debug) System.out.println("looking at node " + pNode);
+
+ if ((pNode instanceof Primitive) &&
+ ((flags & PRIMITIVE) != 0)){
+ if (debug) System.out.println("Primitive found");
+ return pNode;
+ }
+ else if ((pNode instanceof Link) && ((flags & LINK) != 0)){
+ if (debug) System.out.println("Link found");
+ return pNode;
+ }
+ else if ((pNode instanceof Switch) && ((flags & SWITCH) != 0)){
+ if (debug) System.out.println("Switch found");
+ return pNode;
+ }
+ else if ((pNode instanceof TransformGroup) &&
+ ((flags & TRANSFORM_GROUP) != 0)){
+ if (debug) System.out.println("xform group found");
+ return pNode;
+ }
+ else if ((pNode instanceof BranchGroup) &&
+ ((flags & BRANCH_GROUP) != 0)){
+ if (debug) System.out.println("Branch group found");
+ return pNode;
+ }
+ else if ((pNode instanceof Group) && ((flags & GROUP) != 0)){
+ if (debug) System.out.println("Group found");
+ return pNode;
+ }
+ }
+ }
+ return null; // should not be reached
+ }
+
+ /** Extract the picked node from the SceneGraphPath */
+ void storeNode () {
+ if (pickedSceneGraphPath == null) {
+ throw new RuntimeException ("SceneGraphPath missing");
+ }
+ pickedNode = pickedSceneGraphPath.getObject();
+ }
+
+ /** Fill in the intersections of the Node with the PickShape */
+ boolean generateIntersections() {
+ if (geometryArrays == null) {
+ storeGeometry();
+ }
+ intersections = new ArrayList();
+ int hits = 0;
+
+ for (int i = 0; i < geometryArrays.length; i++) {
+ if (intersect(i, firstIntersectOnly)) {
+ if (firstIntersectOnly) {
+ return true;
+ } else {
+ hits++;
+ }
+ }
+ }
+ return (hits > 0);
+ }
+
+
+
+ /* Takes a GeometryArray object, determines what actual type
+ * it is (RTTI) and casts it to call the appropriate intersect method.
+ */
+ final boolean intersect(int geomIndex, boolean firstpick) {
+ int offset;
+ GeometryArray geom = geometryArrays[geomIndex];
+ int numPts = geom.getVertexCount();
+ double[] doubleData = null;
+ float[] floatData = null;
+ Point3d[] p3dData = null;
+ Point3f[] p3fData = null;
+ int vformat = geom.getVertexFormat();
+ int stride;
+ boolean retFlag = false;
+
+ if ((vformat & GeometryArray.BY_REFERENCE) == 0) {
+ doubleData = new double [numPts * 3];
+ geom.getCoordinates (0, doubleData);
+ }
+ else {
+ if ((vformat & GeometryArray.INTERLEAVED) == 0) {
+ doubleData = geom.getCoordRefDouble();
+ // If data was set as float then ..
+ if (doubleData == null) {
+ floatData = geom.getCoordRefFloat();
+ if (floatData == null) {
+ p3fData = geom.getCoordRef3f();
+ if (p3fData == null) {
+ p3dData = geom.getCoordRef3d();
+ }
+ }
+ }
+ }
+ else {
+ floatData = geom.getInterleavedVertices();
+ }
+ }
+
+
+ Point3d[] pnts = new Point3d[numPts];
+
+ /*
+ System.out.println("geomIndex : " + geomIndex);
+ System.out.println("numPts : " + numPts);
+ System.out.println("firstpick : " + firstpick);
+ System.out.println("localToVWorld : ");
+ System.out.println(localToVWorld);
+ */
+
+ if (debug) {
+ System.out.println("localToVWorld = " + localToVWorld);
+ }
+ if ((vformat & GeometryArray.INTERLEAVED) == 0) {
+ if (doubleData != null) {
+ offset = 0;
+ for (int i=0; i < numPts; i++) {
+
+ // Need to transform each pnt by localToVWorld.
+ pnts[i] = getPoint3d();
+ pnts[i].x = doubleData[offset++];
+ pnts[i].y = doubleData[offset++];
+ pnts[i].z = doubleData[offset++];
+
+ localToVWorld.transform(pnts[i]);
+ }
+ }
+ else if (floatData != null) { // by reference and float data is defined ..
+ offset = 0;
+ for (int i=0; i < numPts; i++) {
+
+ // Need to transform each pnt by localToVWorld.
+ pnts[i] = getPoint3d();
+ pnts[i].x = floatData[offset++];
+ pnts[i].y = floatData[offset++];
+ pnts[i].z = floatData[offset++];
+
+ localToVWorld.transform(pnts[i]);
+ }
+ }
+ else if (p3fData != null) {
+ for (int i=0; i < numPts; i++) {
+
+ // Need to transform each pnt by localToVWorld.
+ pnts[i] = getPoint3d();
+ pnts[i].set(p3fData[i]);
+ localToVWorld.transform(pnts[i]);
+ }
+ }
+ else { // p3dData
+ for (int i=0; i < numPts; i++) {
+
+ // Need to transform each pnt by localToVWorld.
+ pnts[i] = getPoint3d();
+ pnts[i].set(p3dData[i]);
+ localToVWorld.transform(pnts[i]);
+ }
+ }
+ }
+ // Its an interleaved type ..
+ else {
+ offset = 0;
+ if ((vformat & GeometryArray.COLOR_3) == GeometryArray.COLOR_3) {
+ offset += 3;
+ }
+ else if ((vformat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4) {
+ offset += 4;
+ }
+ if ((vformat & GeometryArray.NORMALS) != 0)
+ offset += 3;
+ if ((vformat & GeometryArray.TEXTURE_COORDINATE_2) == GeometryArray.TEXTURE_COORDINATE_2) {
+ offset += 2 * geom.getTexCoordSetCount();
+ }
+ else if ((vformat & GeometryArray.TEXTURE_COORDINATE_3) == GeometryArray.TEXTURE_COORDINATE_3) {
+ offset += 3 * geom.getTexCoordSetCount();
+ }
+ stride = offset + 3; // for the vertices .
+ for (int i=0; i < numPts; i++) {
+
+ // Need to transform each pnt by localToVWorld.
+ pnts[i] = getPoint3d();
+ pnts[i].x = floatData[offset];
+ pnts[i].y = floatData[offset+1];
+ pnts[i].z = floatData[offset+2];
+
+ localToVWorld.transform(pnts[i]);
+ offset += stride;
+ }
+ }
+
+ PickIntersection pi = new PickIntersection(this, geom);
+
+ if (geom instanceof PointArray) {
+ retFlag = intersectPA ((PointArray)geom, geomIndex, pnts, firstpick, pi);
+ } else if (geom instanceof IndexedPointArray) {
+ pi.iGeom = (IndexedGeometryArray) geom;
+ retFlag = intersectIPA ((IndexedPointArray)geom, geomIndex, pnts,
+ firstpick, pi);
+ } else if (geom instanceof LineArray) {
+ retFlag = intersectLA ((LineArray)geom, geomIndex, pnts, firstpick, pi);
+ } else if (geom instanceof LineStripArray) {
+ retFlag = intersectLSA ((LineStripArray)geom, geomIndex, pnts,
+ firstpick, pi);
+ } else if (geom instanceof IndexedLineArray) {
+ pi.iGeom = (IndexedGeometryArray) geom;
+ retFlag = intersectILA ((IndexedLineArray)geom, geomIndex, pnts,
+ firstpick, pi);
+ } else if (geom instanceof IndexedLineStripArray) {
+ pi.iGeom = (IndexedGeometryArray) geom;
+ retFlag = intersectILSA ((IndexedLineStripArray)geom, geomIndex, pnts,
+ firstpick, pi);
+ } else if (geom instanceof TriangleArray) {
+ retFlag = intersectTA ((TriangleArray)geom, geomIndex, pnts,
+ firstpick, pi);
+ } else if (geom instanceof TriangleStripArray) {
+ retFlag = intersectTSA ((TriangleStripArray)geom, geomIndex, pnts,
+ firstpick, pi);
+ } else if (geom instanceof TriangleFanArray) {
+ retFlag = intersectTFA ((TriangleFanArray)geom, geomIndex, pnts,
+ firstpick, pi);
+ } else if (geom instanceof IndexedTriangleArray) {
+ pi.iGeom = (IndexedGeometryArray) geom;
+ retFlag = intersectITA ((IndexedTriangleArray)geom, geomIndex, pnts,
+ firstpick, pi);
+ } else if (geom instanceof IndexedTriangleStripArray) {
+ pi.iGeom = (IndexedGeometryArray) geom;
+ retFlag = intersectITSA ((IndexedTriangleStripArray)geom, geomIndex,
+ pnts, firstpick, pi);
+ } else if (geom instanceof IndexedTriangleFanArray) {
+ pi.iGeom = (IndexedGeometryArray) geom;
+ retFlag = intersectITFA ((IndexedTriangleFanArray)geom, geomIndex,
+ pnts, firstpick, pi);
+ } else if (geom instanceof QuadArray) {
+ retFlag = intersectQA ((QuadArray)geom, geomIndex, pnts, firstpick, pi);
+ } else if (geom instanceof IndexedQuadArray) {
+ pi.iGeom = (IndexedGeometryArray) geom;
+ retFlag = intersectIQA ((IndexedQuadArray)geom, geomIndex, pnts,
+ firstpick, pi);
+ } else {
+ throw new RuntimeException ("incorrect class type");
+ }
+
+ if(retFlag == false) {
+ for (int i = 0; i < numPts; i++) {
+ freePoint3d(pnts[i]);
+ }
+ }
+
+ return retFlag;
+
+ }
+
+
+
+ /* ==================================================================== */
+ /* INTERSECT METHODS BY PRIMITIVE TYPE */
+ /* ==================================================================== */
+
+ boolean intersectPoint(int[] vertidx, int[] coordidx, int geomIndex,
+ Point3d[] pnts, PickIntersection pi) {
+ // PickIntersection pi = new PickIntersection(this);
+
+ Point3d[] point = new Point3d[1];
+ point[0] = pnts[coordidx[0]];
+
+ if (debug) {
+ System.out.println("intersect point, point = " + point[0]);
+ }
+
+ boolean intersect = false;
+ switch(pickShapeType) {
+ case PICK_SHAPE_RAY:
+ intersect = intersectPntAndRay(point[0], pickShapeStart,
+ pickShapeDir, pi);
+ break;
+ case PICK_SHAPE_SEGMENT:
+ if (intersectPntAndRay(point[0], pickShapeStart, pickShapeDir, pi)){
+ if(pi.getDistance() <= 1.0) { // TODO: why 1.0?
+ intersect = true;
+ }
+ }
+ break;
+ /* case PICK_SHAPE_POINT:
+ intersect = intersectPntAndPnt(point[0],
+ ((PickPoint) pickShape).location );
+ break;
+ */
+ case PICK_SHAPE_BOUNDING_BOX:
+ intersect = ((BoundingBox)pickShapeBounds).intersect(point[0]);
+ pi.setPointCoordinatesVW(point[0]);
+ break;
+ case PICK_SHAPE_BOUNDING_SPHERE:
+ intersect = ((BoundingSphere)pickShapeBounds).intersect(point[0]);
+ pi.setPointCoordinatesVW(point[0]);
+ break;
+ case PICK_SHAPE_BOUNDING_POLYTOPE:
+ intersect = ((BoundingPolytope)pickShapeBounds).intersect(point[0]);
+ pi.setPointCoordinatesVW(point[0]);
+ break;
+ case PICK_SHAPE_CYLINDER:
+ intersect = intersectCylinder(point[0], (PickCylinder)pickShape,pi);
+ break;
+ case PICK_SHAPE_CONE:
+ intersect = intersectCone (point[0], (PickCone)pickShape, pi);
+ break;
+ }
+ if (intersect) {
+ PickIntersection newpi = new PickIntersection(this, pi.geom);
+ newpi.iGeom = pi.iGeom;
+ newpi.setDistance(pi.distance);
+ newpi.setPointCoordinatesVW(pi.getPointCoordinatesVW());
+
+ // Set PickIntersection parameters
+ newpi.setGeomIndex(geomIndex);
+ newpi.setVertexIndices (vertidx);
+ newpi.setPrimitiveCoordinatesVW(point);
+ intersections.add (newpi);
+ return true;
+ }
+ return false;
+ }
+
+ boolean intersectLine(int[] vertidx, int[] coordidx, int geomIndex,
+ Point3d[] pnts, PickIntersection pi) {
+
+ Point3d[] linePts = new Point3d[2];
+ linePts[0] = pnts[coordidx[0]];
+ linePts[1] = pnts[coordidx[1]];
+
+ boolean intersect = false;
+ switch(pickShapeType) {
+ case PICK_SHAPE_RAY:
+ intersect = intersectLineAndRay(linePts[0], linePts[1],
+ pickShapeStart, pickShapeDir, pi);
+ break;
+ case PICK_SHAPE_SEGMENT:
+ if (intersectLineAndRay(linePts[0], linePts[1], pickShapeStart,
+ pickShapeDir, pi)) {
+ if (pi.getDistance() <= 1.0) {
+ intersect = true;
+ }
+ }
+ break;
+ /* case PICK_SHAPE_POINT:
+ dir.x = linePts[1].x - linePts[0].x;
+ dir.y = linePts[1].y - linePts[0].y;
+ dir.z = linePts[1].z - linePts[0].z;
+ if (intersectPntAndRay(((PickPoint)pickShape).location,
+ pnts[0], dir, dist)) {
+ if(dist[0] <= 1.0) {
+ intersect = true;
+ }
+ }
+ break;
+ */
+ case PICK_SHAPE_BOUNDING_BOX:
+ intersect = intersectBoundingBox(linePts,
+ (BoundingBox)pickShapeBounds);
+ pi.setPointCoordinatesVW(zeroPnt);
+ break;
+ case PICK_SHAPE_BOUNDING_SPHERE:
+ intersect = intersectBoundingSphere(linePts,
+ (BoundingSphere)pickShapeBounds);
+ pi.setPointCoordinatesVW(zeroPnt);
+ break;
+ case PICK_SHAPE_BOUNDING_POLYTOPE:
+ intersect = intersectBoundingPolytope(linePts,
+ (BoundingPolytope)pickShapeBounds);
+ pi.setPointCoordinatesVW(zeroPnt);
+ break;
+ case PICK_SHAPE_CYLINDER:
+ intersect = intersectCylinder (linePts, (PickCylinder)pickShape,pi);
+ break;
+ case PICK_SHAPE_CONE:
+ intersect = intersectCone (linePts, (PickCone) pickShape, pi);
+ break;
+ }
+ if (intersect) {
+ PickIntersection newpi = new PickIntersection(this, pi.geom);
+ newpi.iGeom = pi.iGeom;
+ newpi.setDistance(pi.distance);
+ newpi.setPointCoordinatesVW(pi.getPointCoordinatesVW());
+
+ // Set PickIntersection parameters
+ newpi.setGeomIndex(geomIndex);
+ newpi.setVertexIndices (vertidx);
+ newpi.setPrimitiveCoordinatesVW(linePts);
+ intersections.add (newpi);
+ return true;
+ }
+ return false;
+ }
+
+ boolean intersectTri(int[] vertidx, int[] coordidx, int geomIndex,
+ Point3d[] pnts, PickIntersection pi) {
+
+ Point3d[] triPts = new Point3d[3];
+
+ triPts[0] = pnts[coordidx[0]];
+ triPts[1] = pnts[coordidx[1]];
+ triPts[2] = pnts[coordidx[2]];
+
+
+ boolean intersect = false;
+ switch(pickShapeType) {
+ case PICK_SHAPE_RAY:
+ intersect = intersectRay(triPts, (PickRay) pickShape, pi);
+ break;
+ case PICK_SHAPE_SEGMENT:
+ intersect = intersectSegment(triPts, (PickSegment) pickShape, pi);
+ break;
+ /* case PICK_SHAPE_POINT:
+ if(inside(triPts, (PickPoint) pickShape, ccw)==false)
+ return false;
+ break;
+ */
+ case PICK_SHAPE_BOUNDING_BOX:
+ intersect = intersectBoundingBox (triPts,
+ (BoundingBox)pickShapeBounds);
+ pi.setPointCoordinatesVW(zeroPnt);
+ break;
+ case PICK_SHAPE_BOUNDING_SPHERE:
+ intersect = intersectBoundingSphere (triPts,
+ (BoundingSphere)pickShapeBounds);
+ pi.setPointCoordinatesVW(zeroPnt);
+ break;
+ case PICK_SHAPE_BOUNDING_POLYTOPE:
+ intersect = intersectBoundingPolytope (triPts,
+ (BoundingPolytope)pickShapeBounds);
+ pi.setPointCoordinatesVW(zeroPnt);
+ break;
+ case PICK_SHAPE_CYLINDER:
+ intersect = intersectCylinder (triPts, (PickCylinder) pickShape,pi);
+ break;
+ case PICK_SHAPE_CONE:
+ intersect = intersectCone (triPts, (PickCone)pickShape, pi);
+ break;
+ }
+ if (intersect) {
+ PickIntersection newpi = new PickIntersection(this, pi.geom);
+ newpi.iGeom = pi.iGeom;
+ newpi.setDistance(pi.distance);
+ newpi.setPointCoordinatesVW(pi.getPointCoordinatesVW());
+
+ // Set PickIntersection parameters
+ newpi.setGeomIndex(geomIndex);
+ newpi.setVertexIndices (vertidx);
+
+ newpi.setPrimitiveCoordinatesVW(triPts);
+ intersections.add (newpi);
+ return true;
+ }
+ return false;
+ }
+
+ boolean intersectQuad(int[] vertidx, int[] coordidx, int geomIndex,
+ Point3d[] pnts, PickIntersection pi) {
+
+ Point3d[] quadPts = new Point3d[4];
+
+ quadPts[0] = pnts[coordidx[0]];
+ quadPts[1] = pnts[coordidx[1]];
+ quadPts[2] = pnts[coordidx[2]];
+ quadPts[3] = pnts[coordidx[3]];
+
+ // PickIntersection pi = new PickIntersection(this);
+
+ boolean intersect = false;
+ switch(pickShapeType) {
+ case PICK_SHAPE_RAY:
+ intersect = intersectRay(quadPts, (PickRay) pickShape, pi);
+ break;
+ case PICK_SHAPE_SEGMENT:
+ intersect = intersectSegment(quadPts, (PickSegment) pickShape, pi);
+ break;
+ /* case PICK_SHAPE_POINT:
+ if(inside(quadPts, (PickPoint) pickShape, ccw)==false)
+ return false;
+ break;
+ */
+ case PICK_SHAPE_BOUNDING_BOX:
+ intersect = intersectBoundingBox (quadPts,
+ (BoundingBox)pickShapeBounds);
+ pi.setPointCoordinatesVW(zeroPnt);
+ break;
+ case PICK_SHAPE_BOUNDING_SPHERE:
+ intersect = intersectBoundingSphere (quadPts,
+ (BoundingSphere)pickShapeBounds);
+ pi.setPointCoordinatesVW(zeroPnt);
+ break;
+ case PICK_SHAPE_BOUNDING_POLYTOPE:
+ intersect = intersectBoundingPolytope (quadPts,
+ (BoundingPolytope)pickShapeBounds);
+ pi.setPointCoordinatesVW(zeroPnt);
+ break;
+ case PICK_SHAPE_CYLINDER:
+ intersect = intersectCylinder (quadPts, (PickCylinder)pickShape,pi);
+ break;
+ case PICK_SHAPE_CONE:
+ intersect = intersectCone (quadPts, (PickCone)pickShape, pi);
+ break;
+ }
+ if (intersect) {
+ PickIntersection newpi = new PickIntersection(this, pi.geom);
+ newpi.iGeom = pi.iGeom;
+ newpi.setDistance(pi.distance);
+ newpi.setPointCoordinatesVW(pi.getPointCoordinatesVW());
+
+ // Set PickIntersection parameters
+ newpi.setGeomIndex(geomIndex);
+ newpi.setVertexIndices (vertidx);
+ newpi.setPrimitiveCoordinatesVW(quadPts);
+ intersections.add (newpi);
+ return true;
+ }
+ return false;
+ }
+
+ /* ==================================================================== */
+ /* INTERSECT METHODS BY GEOMETRY TYPE */
+ /* ==================================================================== */
+
+ /**
+ Intersect method for PointArray
+ */
+ boolean intersectPA (PointArray geom, int geomIndex, Point3d[] pnts,
+ boolean firstpick, PickIntersection pi) {
+
+ if (debug) System.out.println ("intersect: PointArray");
+
+ int[] pntVertIdx = new int[1];
+ int numint = 0;
+
+ for (int i = 0; i < pnts.length; i++) {
+ pntVertIdx[0] = i;
+ if (intersectPoint(pntVertIdx, pntVertIdx, geomIndex, pnts, pi)) {
+ numint++;
+ if (firstpick) return true;
+ }
+ }
+ if (numint > 0) return true;
+ return false;
+ }
+
+ /**
+ Intersect method for IndexedPointArray
+ */
+ boolean intersectIPA (IndexedPointArray geom, int geomIndex, Point3d[] pnts,
+ boolean firstpick, PickIntersection pi) {
+
+ if (debug) System.out.println ("intersect: IndexedPointArray");
+
+ int[] pntVertIdx = new int[1];
+ int[] pntCoordIdx = new int[1];
+
+ int numint = 0;
+ int indexCount = geom.getIndexCount();
+
+ for (int i=0; i< indexCount; i++) {
+ pntVertIdx[0] = i;
+ pntCoordIdx[0] = geom.getCoordinateIndex(i);
+ if (intersectPoint(pntVertIdx, pntCoordIdx, geomIndex, pnts, pi)) {
+ numint++;
+ if (firstpick) return true;
+ }
+ }
+ if (numint > 0) return true;
+ return false;
+ }
+
+
+ /**
+ Intersect method for LineArray
+ */
+ /**
+ Intersect method for LineArray
+ */
+ boolean intersectLA (LineArray geom, int geomIndex, Point3d[] pnts,
+ boolean firstpick, PickIntersection pi) {
+
+ if (debug) System.out.println ("intersect: LineArray");
+
+ int[] lineVertIdx = new int[2];
+
+ int numint = 0;
+
+ for (int i=0; i< pnts.length;) {
+ /* set up the parameters for the current line */
+ lineVertIdx[0] = i++;
+ lineVertIdx[1] = i++;
+ if (intersectLine(lineVertIdx, lineVertIdx, geomIndex, pnts, pi)) {
+ numint++;
+ if (firstpick) return true;
+ }
+ }
+ if (numint > 0) return true;
+ return false;
+ }
+
+ /**
+ Intersect method for LineStripArray
+ */
+ boolean intersectLSA (LineStripArray geom, int geomIndex, Point3d[] pnts,
+ boolean firstpick, PickIntersection pi) {
+ int numint = 0;
+
+ int[] stripVertexCounts = new int [geom.getNumStrips()];
+ geom.getStripVertexCounts (stripVertexCounts);
+ int stripStart = 0;
+
+ if (debug) System.out.println ("intersect: LineStripArray");
+
+ int[] lineVertIdx = new int[2];
+
+ for (int i=0; i < stripVertexCounts.length; i++) {
+ lineVertIdx[0] = stripStart;
+ int end = stripStart + stripVertexCounts[i];
+
+ for (int j=stripStart+1; j<end; j++) {
+ lineVertIdx[1] = j;
+ if (intersectLine(lineVertIdx, lineVertIdx, geomIndex, pnts, pi)) {
+ numint++;
+ if (firstpick) return true;
+ }
+ lineVertIdx[0] = lineVertIdx[1];
+ }
+ stripStart += stripVertexCounts[i];
+ }
+ if (numint > 0) return true;
+ return false;
+ }
+
+ /**
+ Intersect method for IndexedLineArray
+ */
+ boolean intersectILA (IndexedLineArray geom, int geomIndex, Point3d[] pnts,
+ boolean firstpick, PickIntersection pi) {
+
+ int numint = 0;
+ int indexCount = geom.getIndexCount();
+ if (debug) System.out.println ("intersect: IndexedLineArray");
+
+ int[] lineVertIdx = new int[2];
+ int[] lineCoordIdx = new int[2];
+
+ for (int i=0; i<indexCount;) {
+ lineVertIdx[0] = i;
+ lineCoordIdx[0] = geom.getCoordinateIndex(i++);
+ lineVertIdx[1] = i;
+ lineCoordIdx[1] = geom.getCoordinateIndex(i++);
+ if (intersectLine(lineVertIdx, lineCoordIdx, geomIndex, pnts, pi)) {
+ numint++;
+ if (firstpick) return true;
+ }
+ }
+ if (numint > 0) return true;
+ return false;
+ }
+
+ /**
+ Intersect method for IndexedLineStripArray
+ */
+ boolean intersectILSA (IndexedLineStripArray geom, int geomIndex,
+ Point3d[] pnts, boolean firstpick, PickIntersection pi) {
+ if (debug) System.out.println ("intersect: IndexedLineStripArray");
+
+ int[] lineVertIdx = new int[2];
+ int[] lineCoordIdx = new int[2];
+
+ int numint = 0;
+ int[] stripVertexCounts = new int [geom.getNumStrips()];
+ geom.getStripIndexCounts (stripVertexCounts);
+ int stripStart = 0;
+
+ for (int i=0; i < stripVertexCounts.length; i++) {
+
+ lineVertIdx[0] = stripStart;
+ lineCoordIdx[0] = geom.getCoordinateIndex(stripStart);
+ int end = stripStart + stripVertexCounts[i];
+ for (int j=stripStart+1; j<end; j++) {
+ lineVertIdx[1] = j;
+ lineCoordIdx[1] = geom.getCoordinateIndex(j);
+ if (intersectLine(lineVertIdx, lineCoordIdx, geomIndex, pnts, pi)) {
+ numint++;
+ if (firstpick) return true;
+ }
+ lineVertIdx[0] = lineVertIdx[1];
+ lineCoordIdx[0] = lineCoordIdx[1];
+ }
+ stripStart += stripVertexCounts[i];
+ }
+ if (numint > 0) return true;
+ return false;
+ }
+
+ /**
+ Intersect method for TriangleArray
+ */
+ boolean intersectTA (TriangleArray geom, int geomIndex, Point3d[] pnts,
+ boolean firstpick, PickIntersection pi) {
+
+ if (debug)
+ System.out.println ("intersect: TriangleArray");
+
+ int[] triVertIdx = new int[3];
+
+ int numint = 0;
+ for (int i=0; i<pnts.length;) {
+ triVertIdx[0] = i++;
+ triVertIdx[1] = i++;
+ triVertIdx[2] = i++;
+ if (intersectTri(triVertIdx, triVertIdx, geomIndex, pnts, pi)) {
+ numint++;
+ if (firstpick) return true;
+ }
+ }
+
+ if (numint > 0) return true;
+ return false;
+ }
+
+ /**
+ Intersect method for IndexedTriangleArray
+ */
+ boolean intersectITA (IndexedTriangleArray geom, int geomIndex,
+ Point3d[] pnts, boolean firstpick, PickIntersection pi) {
+
+ if (debug)
+ System.out.println ("intersect: IndexedTriangleArray");
+
+ int[] triVertIdx = new int[3];
+ int[] triCoordIdx = new int[3];
+
+ int numint = 0;
+ int indexCount = geom.getIndexCount();
+ for (int i=0; i<indexCount;) {
+ triVertIdx[0] = i;
+ triCoordIdx[0] = geom.getCoordinateIndex(i++);
+ triVertIdx[1] = i;
+ triCoordIdx[1] = geom.getCoordinateIndex(i++);
+ triVertIdx[2] = i;
+ triCoordIdx[2] = geom.getCoordinateIndex(i++);
+ if (intersectTri(triVertIdx, triCoordIdx, geomIndex, pnts, pi)) {
+ numint++;
+ if (firstpick) return true;
+ }
+ }
+
+ if (numint > 0) return true;
+ return false;
+ }
+
+ /**
+ Intersect method for TriangleStripArray
+ */
+ boolean intersectTSA (TriangleStripArray geom, int geomIndex,
+ Point3d[] pnts, boolean firstpick, PickIntersection pi) {
+ if (debug)
+ System.out.println ("intersect: TriangleStripArray");
+
+ boolean ccw;
+ int numint = 0;
+ int[] stripVertexCounts = new int [geom.getNumStrips()];
+ geom.getStripVertexCounts (stripVertexCounts);
+ int stripStart = 0;
+ int start;
+ int[] triVertIdx = new int[3];
+
+ for (int i=0; i<stripVertexCounts.length; i++) {
+
+ start = stripStart;
+ // start a new strip
+ ccw = true;
+ triVertIdx[0] = start++;
+ triVertIdx[1] = start++;
+
+ int end = start + stripVertexCounts[i] - 2;
+ for (int j=start; j< end; j++) {
+ /*
+ if (ccw) {
+ triVertIdx[2] = j;
+ } else {
+ triVertIdx[1] = j;
+ }
+ */
+ triVertIdx[2] = j;
+ if (intersectTri(triVertIdx, triVertIdx, geomIndex, pnts, pi)) {
+ numint++;
+ if (firstpick) return true;
+ }
+
+ // Advance to the next triangle, keeping the winding of the test
+ // triangle correct.
+ /*
+ if (ccw) {
+ triVertIdx[0] = triVertIdx[1];
+ // triVertIdx[2] remains, triVertIdx[1] will be replaced
+ ccw = false;
+ } else {
+ triVertIdx[0] = triVertIdx[2];
+ // triVertIdx[1] remains, triVertIdx[2] will be replaced
+ ccw = true;
+ }
+ */
+ triVertIdx[0] = triVertIdx[1];
+ triVertIdx[1] = triVertIdx[2];
+
+ }
+ stripStart += stripVertexCounts[i];
+ }
+
+ if (numint > 0) return true;
+ return false;
+ }
+
+ /**
+ Intersect method for IndexedTriangleStripArray
+ */
+ boolean intersectITSA (IndexedTriangleStripArray geom, int geomIndex,
+ Point3d[] pnts, boolean firstpick, PickIntersection pi) {
+
+ if (debug)
+ System.out.println ("intersect: IndexedTriangleStripArray");
+ int numint = 0;
+ boolean ccw;
+
+ int[] stripVertexCounts = new int [geom.getNumStrips()];
+ geom.getStripIndexCounts (stripVertexCounts);
+ int stripStart = 0;
+ int start;
+ int[] triVertIdx = new int[3];
+ int[] triCoordIdx = new int[3];
+
+ for (int i=0; i<stripVertexCounts.length; i++) {
+
+ start = stripStart;
+ // start a new strip
+ ccw = true;
+ triCoordIdx[0] = geom.getCoordinateIndex(start);
+ triVertIdx[0] = start++;
+ triCoordIdx[1] = geom.getCoordinateIndex(start);
+ triVertIdx[1] = start++;
+
+ int end = start + stripVertexCounts[i] - 2;
+ for (int j=start; j<end; j++) {
+ if (ccw) {
+ triVertIdx[2] = j;
+ triCoordIdx[2] = geom.getCoordinateIndex(j);
+ } else {
+ triVertIdx[1] = j;
+ triCoordIdx[1] = geom.getCoordinateIndex(j);
+ }
+
+ if (intersectTri(triVertIdx, triCoordIdx, geomIndex, pnts, pi)) {
+ numint++;
+ if (firstpick) return true;
+ }
+
+ // Advance to the next triangle, keeping the winding of the test
+ // triangle correct.
+ if (ccw) {
+ triVertIdx[0] = triVertIdx[1];
+ // triVertIdx[2] remains, triVertIdx[1] will be replaced
+ triCoordIdx[0] = triCoordIdx[1];
+ ccw = false;
+ } else {
+ triVertIdx[0] = triVertIdx[2];
+ // triVertIdx[1] remains, triVertIdx[2] will be replaced
+ triCoordIdx[0] = triCoordIdx[2];
+ ccw = true;
+ }
+ }
+ stripStart += stripVertexCounts[i];
+ }
+
+ if (numint > 0) return true;
+ return false;
+
+ }
+
+ /**
+ Intersect method for TriangleFanArray
+ */
+ boolean intersectTFA (TriangleFanArray geom, int geomIndex, Point3d[] pnts,
+ boolean firstpick, PickIntersection pi) {
+
+ if (debug) System.out.println("intersect: TriangleFanArray");
+
+ int numint = 0;
+
+ int[] stripVertexCounts = new int [geom.getNumStrips()];
+ geom.getStripVertexCounts (stripVertexCounts);
+ int fanStart = 0;
+ int start;
+ int[] triVertIdx = new int[3];
+
+ // System.out.println("stripVertexCounts.length " + stripVertexCounts.length);
+ for (int i=0; i<stripVertexCounts.length; i++) {
+
+ start = fanStart;
+ triVertIdx[0] = start++;
+ triVertIdx[1] = start++;
+
+ int end = start + stripVertexCounts[i] - 2;
+ for (int j=start; j<end; j++) {
+ triVertIdx[2] = j;
+ if (intersectTri(triVertIdx, triVertIdx, geomIndex, pnts, pi)) {
+ numint++;
+ if (firstpick) return true;
+ }
+ triVertIdx[1] = triVertIdx[2];
+ }
+ fanStart += stripVertexCounts[i];
+ }
+ if (numint > 0) return true;
+ return false;
+ }
+
+ /**
+ Intersect method for IndexedTriangleFanArray
+ */
+ boolean intersectITFA (IndexedTriangleFanArray geom, int geomIndex,
+ Point3d[] pnts, boolean firstpick, PickIntersection pi) {
+
+ if (debug) System.out.println ("intersect: IndexedTriangleFanArray");
+
+ int numint = 0;
+ int[] stripVertexCounts = new int [geom.getNumStrips()];
+ geom.getStripIndexCounts (stripVertexCounts);
+ int fanStart = 0;
+ int start;
+ int[] triVertIdx = new int[3];
+ int[] triCoordIdx = new int[3];
+
+ for (int i=0; i<stripVertexCounts.length; i++) {
+
+ start = fanStart;
+ triCoordIdx[0] = geom.getCoordinateIndex(start);
+ triVertIdx[0] = start++;
+ triCoordIdx[1] = geom.getCoordinateIndex(start);
+ triVertIdx[1] = start++;
+
+ int end = start + stripVertexCounts[i] - 2;
+ for (int j=start; j<end; j++) {
+ triVertIdx[2] = j;
+ triCoordIdx[2] = geom.getCoordinateIndex(j);
+ if (intersectTri(triVertIdx, triCoordIdx, geomIndex, pnts, pi)) {
+ numint++;
+ if (firstpick) return true;
+ }
+ triVertIdx[1] = triVertIdx[2];
+ triCoordIdx[1] = triCoordIdx[2];
+ }
+ fanStart += stripVertexCounts[i];
+ }
+ if (numint > 0) return true;
+ return false;
+ }
+
+ /**
+ Intersect method for QuadArray
+ */
+ boolean intersectQA (QuadArray geom, int geomIndex, Point3d[] pnts,
+ boolean firstpick, PickIntersection pi) {
+
+ if (debug) System.out.println ("intersect: QuadArray");
+
+ int[] quadVertIdx = new int[4];
+
+ int numint = 0;
+ for (int i=0; i<pnts.length;) {
+ quadVertIdx[0] = i++;
+ quadVertIdx[1] = i++;
+ quadVertIdx[2] = i++;
+ quadVertIdx[3] = i++;
+ if (intersectQuad(quadVertIdx, quadVertIdx, geomIndex, pnts, pi)) {
+ numint++;
+ if (firstpick) return true;
+ }
+ }
+
+ if (numint > 0) return true;
+ return false;
+ }
+
+ /**
+ Intersect method for IndexedQuadArray
+ */
+ final boolean intersectIQA (IndexedQuadArray geom, int geomIndex,
+ Point3d[] pnts, boolean firstpick,
+ PickIntersection pi) {
+
+ if (debug) System.out.println ("intersect: IndexedQuadArray");
+
+ int[] quadVertIdx = new int[4];
+ int[] quadCoordIdx = new int[4];
+
+ int numint = 0;
+ int indexCount = geom.getIndexCount();
+ // System.out.println ("intersect: IndexedQuadArray : indexCount " + indexCount);
+ for (int i=0; i<indexCount;) {
+ quadVertIdx[0] = i;
+ quadCoordIdx[0] = geom.getCoordinateIndex(i++);
+ quadVertIdx[1] = i;
+ quadCoordIdx[1] = geom.getCoordinateIndex(i++);
+ quadVertIdx[2] = i;
+ quadCoordIdx[2] = geom.getCoordinateIndex(i++);
+ quadVertIdx[3] = i;
+ quadCoordIdx[3] = geom.getCoordinateIndex(i++);
+
+ if (intersectQuad(quadVertIdx, quadCoordIdx, geomIndex, pnts, pi)) {
+ numint++;
+ if (firstpick) return true;
+ }
+ }
+
+ if (numint > 0) return true;
+ return false;
+
+ }
+
+ /* ==================================================================== */
+ /* GENERAL INTERSECT METHODS */
+ /* ==================================================================== */
+ static boolean intersectBoundingBox (Point3d coordinates[],
+ BoundingBox box) {
+ int i, j;
+ int out[] = new int[6];
+
+ Point3d lower = new Point3d();
+ Point3d upper = new Point3d();
+ box.getLower (lower);
+ box.getUpper (upper);
+
+ //Do trivial vertex test.
+ for (i=0; i<6; i++) out[i] = 0;
+ for (i=0; i<coordinates.length; i++) {
+ if ((coordinates[i].x >= lower.x) &&
+ (coordinates[i].x <= upper.x) &&
+ (coordinates[i].y >= lower.y) &&
+ (coordinates[i].y <= upper.y) &&
+ (coordinates[i].z >= lower.z) &&
+ (coordinates[i].z <= upper.z)) {
+ // We're done! It's inside the boundingbox.
+ return true;
+ } else {
+ if (coordinates[i].x < lower.x) out[0]++; // left
+ if (coordinates[i].y < lower.y) out[1]++; // bottom
+ if (coordinates[i].z < lower.z) out[2]++; // back
+ if (coordinates[i].x > upper.x) out[3]++; // right
+ if (coordinates[i].y > upper.y) out[4]++; // top
+ if (coordinates[i].z > upper.z) out[5]++; // front
+ }
+ }
+
+ if ((out[0] == coordinates.length) || (out[1] == coordinates.length) ||
+ (out[2] == coordinates.length) || (out[3] == coordinates.length) ||
+ (out[4] == coordinates.length) || (out[5] == coordinates.length)){
+ // we're done. primitive is outside of boundingbox.
+ return false;
+ }
+ // Setup bounding planes.
+ Point3d pCoor[] = new Point3d[4];
+ for (i=0; i<4; i++) pCoor[i] = new Point3d();
+
+ // left plane.
+ pCoor[0].set(lower.x, lower.y, lower.z);
+ pCoor[1].set(lower.x, lower.y, upper.z);
+ pCoor[2].set(lower.x, upper.y, upper.z);
+ pCoor[3].set(lower.x, upper.y, lower.z);
+ if (intersectPolygon(pCoor, coordinates, false) == true) return true;
+
+ // right plane.
+ pCoor[0].set(upper.x, lower.y, lower.z);
+ pCoor[1].set(upper.x, upper.y, lower.z);
+ pCoor[2].set(upper.x, upper.y, upper.z);
+ pCoor[3].set(upper.x, lower.y, upper.z);
+ if (intersectPolygon(pCoor, coordinates, false) == true) return true;
+
+ // bottom plane.
+ pCoor[0].set(upper.x, lower.y, upper.z);
+ pCoor[1].set(lower.x, lower.y, upper.z);
+ pCoor[2].set(lower.x, lower.y, lower.z);
+ pCoor[3].set(upper.x, lower.y, lower.z);
+ if (intersectPolygon(pCoor, coordinates, false) == true) return true;
+
+ // top plane.
+ pCoor[0].set(upper.x, upper.y, upper.z);
+ pCoor[1].set(upper.x, upper.y, lower.z);
+ pCoor[2].set(lower.x, upper.y, lower.z);
+ pCoor[3].set(lower.x, upper.y, upper.z);
+ if (intersectPolygon(pCoor, coordinates, false) == true) return true;
+
+ // front plane.
+ pCoor[0].set(upper.x, upper.y, upper.z);
+ pCoor[1].set(lower.x, upper.y, upper.z);
+ pCoor[2].set(lower.x, lower.y, upper.z);
+ pCoor[3].set(upper.x, lower.y, upper.z);
+ if (intersectPolygon(pCoor, coordinates, false) == true) return true;
+
+ // back plane.
+ pCoor[0].set(upper.x, upper.y, lower.z);
+ pCoor[1].set(upper.x, lower.y, lower.z);
+ pCoor[2].set(lower.x, lower.y, lower.z);
+ pCoor[3].set(lower.x, upper.y, lower.z);
+ if (intersectPolygon(pCoor, coordinates, false) == true) return true;
+
+ return false;
+ }
+
+ static boolean intersectBoundingSphere (Point3d coordinates[],
+ BoundingSphere sphere) {
+
+
+ int i, j;
+ Vector3d tempV3D = new Vector3d();
+ boolean esFlag;
+ Point3d center = new Point3d();
+ sphere.getCenter (center);
+ double radius = sphere.getRadius ();
+ //Do trivial vertex test.
+
+ for (i=0; i<coordinates.length; i++) {
+ tempV3D.x = coordinates[i].x - center.x;
+ tempV3D.y = coordinates[i].y - center.y;
+ tempV3D.z = coordinates[i].z - center.z;
+
+ if (tempV3D.length() <= radius) {
+ // We're done! It's inside the boundingSphere.
+ return true;
+ }
+ }
+
+ for (i=0; i<coordinates.length; i++) {
+ if (i < (coordinates.length-1)) {
+ esFlag = edgeIntersectSphere(sphere, coordinates[i],
+ coordinates[i+1]);
+ } else {
+ esFlag = edgeIntersectSphere(sphere, coordinates[i],
+ coordinates[0]);
+ }
+ if (esFlag == true) {
+ return true;
+ }
+ }
+
+ if (coordinates.length < 3) return false; // We're done with line.
+
+ // Find rho.
+ // Compute plane normal.
+ Vector3d vec0 = new Vector3d(); //Edge vector from point 0 to point 1;
+ Vector3d vec1 = new Vector3d(); //Edge vector from point 0 to point 2 or 3;
+ Vector3d pNrm = new Vector3d();
+ Vector3d pa = new Vector3d();
+ Point3d q = new Point3d();
+ double nLenSq, pqLen, pNrmDotPa, tq;
+
+ // compute plane normal for coordinates.
+ for (i=0; i<coordinates.length-1;) {
+ vec0.x = coordinates[i+1].x - coordinates[i].x;
+ vec0.y = coordinates[i+1].y - coordinates[i].y;
+ vec0.z = coordinates[i+1].z - coordinates[i++].z;
+ if (vec0.length() > 0.0) break;
+ }
+
+ for (j=i; j<coordinates.length-1; j++) {
+ vec1.x = coordinates[j+1].x - coordinates[j].x;
+ vec1.y = coordinates[j+1].y - coordinates[j].y;
+ vec1.z = coordinates[j+1].z - coordinates[j].z;
+ if (vec1.length() > 0.0) break;
+ }
+
+ if (j == (coordinates.length-1)) {
+ // System.out.println("(1) Degenerated polygon.");
+ return false; // Degenerated polygon.
+ }
+
+ /*
+ for (i=0; i<coordinates.length; i++)
+ System.out.println("coordinates P" + i + " " + coordinates[i]);
+ for (i=0; i<coord2.length; i++)
+ System.out.println("coord2 P" + i + " " + coord2[i]);
+ */
+
+ pNrm.cross(vec0,vec1);
+
+ nLenSq = pNrm.lengthSquared();
+ if ( nLenSq == 0.0) {
+ // System.out.println("(2) Degenerated polygon.");
+ return false; // Degenerated polygon.
+ }
+
+ pa.x = coordinates[0].x - center.x;
+ pa.y = coordinates[0].y - center.y;
+ pa.z = coordinates[0].z - center.z;
+
+ pNrmDotPa = pNrm.dot(pa);
+
+ pqLen = Math.sqrt(pNrmDotPa * pNrmDotPa/ nLenSq);
+
+ if (pqLen > radius)
+ return false;
+
+ tq = pNrmDotPa / nLenSq;
+
+ q.x = center.x + tq * pNrm.x;
+ q.y = center.y + tq * pNrm.y;
+ q.z = center.z + tq * pNrm.z;
+
+ // PolyPnt2D Test.
+ return pointIntersectPolygon2D( pNrm, coordinates, q);
+ }
+
+ static boolean intersectBoundingPolytope (Point3d coordinates[],
+ BoundingPolytope polytope) {
+
+ boolean debug = false;
+
+ // this is a multiplier to the halfplane distance coefficients
+ double distanceSign = -1.0;
+ // Variable needed for intersection.
+ Point4d tP4d = new Point4d();
+
+ Vector4d[] planes = new Vector4d [polytope.getNumPlanes()];
+ polytope.getPlanes (planes);
+
+ if (coordinates.length == 2) {
+ // we'll handle line separately.
+ throw new java.lang.RuntimeException ("TODO: must make polytope.intersect(coordinates[0], coordinates[1], tP4d) public!");
+ // TODO: must make this public !!!
+ // return polytope.intersect(coordinates[0], coordinates[1], tP4d );
+ }
+
+ // It is a triangle or a quad.
+
+ // first test to see if any of the coordinates are all inside of the
+ // intersection polytope's half planes
+ // essentially do a matrix multiply of the constraintMatrix K*3 with
+ // the input coordinates 3*1 = K*1 vector
+
+ if (debug) {
+ System.out.println("The value of the input vertices are: ");
+ for (int i=0; i < coordinates.length; i++) {
+ System.out.println("The " +i+ " th vertex is: " + coordinates[i]);
+ }
+
+ System.out.println("The value of the input bounding Polytope's planes =");
+ for (int i=0; i < planes.length; i++) {
+ System.out.println("The " +i+ " th plane is: " + planes[i]);
+ }
+
+ }
+
+ // the direction for the intersection cost function
+ double centers[] = new double[4];
+ centers[0] = 0.8; centers[1] = 0.9; centers[2] = 1.1; centers[3] = 1.2;
+
+ boolean intersection = true;
+ boolean PreTest = false;
+
+ if (PreTest) {
+ // for each coordinate, test it with each half plane
+ for (int i=0; i < coordinates.length; i++) {
+ for (int j=0; j < planes.length; j++) {
+ if ((planes[j].x * coordinates[i].x +
+ planes[j].y * coordinates[i].y +
+ planes[j].z*coordinates[i].z) <=
+ (distanceSign)*planes[j].w){
+ // the point satisfies this particular hyperplane
+ intersection = true;
+ } else {
+ // the point fails this hyper plane try with a new hyper plane
+ intersection = false;
+ break;
+ }
+ }
+ if (intersection) {
+ // a point was found to be completely inside the bounding hull
+ return true;
+ }
+ }
+ } // end of pretest
+
+ // at this point all points are outside of the bounding hull
+ // build the problem tableau for the linear program
+
+ int numberCols = planes.length + 2 + coordinates.length + 1;
+ int numberRows = 1 + coordinates.length;
+
+ double problemTableau[][] = new double[numberRows][numberCols];
+
+ // compute -Mtrans = -A*P
+ for ( int i = 0; i < planes.length; i++) {
+ for ( int j=0; j < coordinates.length; j++) {
+ problemTableau[j][i] = (-1.0)* (planes[i].x*coordinates[j].x+
+ planes[i].y*coordinates[j].y+
+ planes[i].z*coordinates[j].z);
+ }
+ }
+
+ // add the other rows
+ for (int i = 0; i < coordinates.length; i++) {
+ problemTableau[i][planes.length] = -1.0;
+ problemTableau[i][planes.length + 1] = 1.0;
+
+ for (int j=0; j < coordinates.length; j++) {
+ if ( i==j ) {
+ problemTableau[i][j + planes.length + 2] = 1.0;
+ } else {
+ problemTableau[i][j + planes.length + 2] = 0.0;
+ }
+
+ // place the last column elements the Ci's
+ problemTableau[i][numberCols - 1] = centers[i];
+ }
+ }
+
+ // place the final rows value
+ for (int j = 0; j < planes.length; j++) {
+ problemTableau[numberRows - 1][j] =
+ (distanceSign)*planes[j].w;
+ }
+ problemTableau[numberRows - 1][planes.length] = 1.0;
+ problemTableau[numberRows - 1][planes.length+1] = -1.0;
+ for (int j = 0; j < coordinates.length; j++) {
+ problemTableau[numberRows - 1][planes.length+2+j] = 0.0;
+ }
+
+ if (debug) {
+ System.out.println("The value of the problem tableau is: " );
+ for (int i=0; i < problemTableau.length; i++) {
+ for (int j=0; j < problemTableau[0].length; j++) {
+ System.out.print(problemTableau[i][j] + " ");
+ }
+ System.out.println();
+ }
+ }
+
+ double distance = generalStandardSimplexSolver(problemTableau,
+ Float.NEGATIVE_INFINITY);
+ if (debug) {
+ System.out.println("The value returned by the general standard simplex = " +
+ distance);
+ }
+ if (distance == Float.POSITIVE_INFINITY) {
+ return false;
+ }
+ return true;
+ }
+
+
+ // optimized version using arrays of doubles, but using the standard simplex
+ // method to solve the LP tableau. This version has not been optimized to
+ // work with a particular size input tableau and is much slower than some
+ // of the other variants...supposedly
+ static double generalStandardSimplexSolver(double problemTableau[][],
+ double stopingValue) {
+ boolean debug = false;
+ int numRow = problemTableau.length;
+ int numCol = problemTableau[0].length;
+ boolean optimal = false;
+ int i, pivotRowIndex, pivotColIndex;
+ double maxElement, element, endElement, ratio, prevRatio;
+ int count = 0;
+ double multiplier;
+
+ if (debug) {
+ System.out.println("The number of rows is : " + numRow);
+ System.out.println("The number of columns is : " + numCol);
+ }
+
+ // until the optimal solution is found continue to do
+ // iterations of the simplex method
+ while(!optimal) {
+
+ if (debug) {
+ System.out.println("input problem tableau is:");
+ for (int k=0; k < numRow; k++) {
+ for (int j=0; j < numCol; j++) {
+ System.out.println("kth, jth value is:" +k+" "+j+" : " +
+ problemTableau[k][j]);
+ }
+ }
+ }
+
+ // test to see if the current solution is optimal
+ // check all bottom row elements except the right most one and
+ // if all positive or zero its optimal
+ for (i = 0, maxElement = 0, pivotColIndex = -1; i < numCol - 1; i++) {
+ // a bottom row element
+ element = problemTableau[numRow - 1][i];
+ if ( element < maxElement) {
+ maxElement = element;
+ pivotColIndex = i;
+ }
+ }
+
+ // if there is no negative non-zero element then we
+ // have found an optimal solution (the last row of the tableau)
+ if (pivotColIndex == -1) {
+ // found an optimal solution
+ //System.out.println("Found an optimal solution");
+ optimal = true;
+ }
+
+ //System.out.println("The value of maxElement is:" + maxElement);
+
+ if (!optimal) {
+ // Case when the solution is not optimal but not known to be
+ // either unbounded or infeasable
+
+ // from the above we have found the maximum negative element in
+ // bottom row, we have also found the column for this value
+ // the pivotColIndex represents this
+
+ // initialize the values for the algorithm, -1 for pivotRowIndex
+ // indicates no solution
+
+ prevRatio = Float.POSITIVE_INFINITY;
+ ratio = 0.0;
+ pivotRowIndex = -1;
+
+ // note if all of the elements in the pivot column are zero or
+ // negative the problem is unbounded.
+ for (i = 0; i < numRow - 1; i++) {
+ element = problemTableau[i][pivotColIndex]; // r value
+ endElement = problemTableau[i][numCol-1]; // s value
+
+ // pivot according to the rule that we want to choose the row
+ // with smallest s/r ratio see third case
+ // currently we ignore valuse of r==0 (case 1) and cases where the
+ // ratio is negative, i.e. either r or s are negative (case 2)
+ if (element == 0) {
+ if (debug) {
+ System.out.println("Division by zero has occurred");
+ System.out.println("Within the linear program solver");
+ System.out.println("Ignoring the zero as a potential pivot");
+ }
+ } else if ( (element < 0.0) || (endElement < 0.0) ){
+ if (debug) {
+ System.out.println("Ignoring cases where element is negative");
+ System.out.println("The value of element is: " + element);
+ System.out.println("The value of end Element is: " + endElement);
+ }
+ } else {
+ ratio = endElement/element; // should be s/r
+ if (debug) {
+ System.out.println("The value of element is: " + element);
+ System.out.println("The value of endElement is: " + endElement);
+ System.out.println("The value of ratio is: " + ratio);
+ System.out.println("The value of prevRatio is: " + prevRatio);
+ System.out.println("Value of ratio <= prevRatio is :" +
+ (ratio <= prevRatio));
+ }
+ if (ratio <= prevRatio) {
+ if (debug) {
+ System.out.println("updating prevRatio with ratio");
+ }
+ prevRatio = ratio;
+ pivotRowIndex = i;
+ }
+ }
+ }
+
+ // if the pivotRowIndex is still -1 then we know the pivotColumn
+ // has no viable pivot points and the solution is unbounded or
+ // infeasable (all pivot elements were either zero or negative or
+ // the right most value was negative (the later shouldn't happen?)
+ if (pivotRowIndex == -1) {
+ if (debug) {
+ System.out.println("UNABLE TO FIND SOLUTION");
+ System.out.println("The system is infeasable or unbounded");
+ }
+ return(Float.POSITIVE_INFINITY);
+ }
+
+ // we now have the pivot row and col all that remains is
+ // to divide through by this value and subtract the appropriate
+ // multiple of the pivot row from all other rows to obtain
+ // a tableau which has a column of all zeros and one 1 in the
+ // intersection of pivot row and col
+
+ // divide through by the pivot value
+ double pivotValue = problemTableau[pivotRowIndex][pivotColIndex];
+
+ if (debug) {
+ System.out.println("The value of row index is: " + pivotRowIndex);
+ System.out.println("The value of col index is: " + pivotColIndex);
+ System.out.println("The value of pivotValue is: " + pivotValue);
+ }
+ // divide through by s on the pivot row to obtain a 1 in pivot col
+ for (i = 0; i < numCol; i++) {
+ problemTableau[pivotRowIndex][i] =
+ problemTableau[pivotRowIndex][i] / pivotValue;
+ }
+
+ // subtract appropriate multiple of pivot row from all other rows
+ // to zero out all rows except the final row and the pivot row
+ for (i = 0; i < numRow; i++) {
+ if (i != pivotRowIndex) {
+ multiplier = problemTableau[i][pivotColIndex];
+ for (int j=0; j < numCol; j++) {
+ problemTableau[i][j] = problemTableau[i][j] -
+ multiplier * problemTableau[pivotRowIndex][j];
+ }
+ }
+ }
+ }
+ // case when the element is optimal
+ }
+ return(problemTableau[numRow - 1][numCol - 1]);
+ }
+
+ static boolean edgeIntersectSphere (BoundingSphere sphere, Point3d start,
+ Point3d end) {
+
+ double abLenSq, acLenSq, apLenSq, abDotAp, radiusSq;
+ Vector3d ab = new Vector3d();
+ Vector3d ap = new Vector3d();
+
+ Point3d center = new Point3d();
+ sphere.getCenter (center);
+ double radius = sphere.getRadius ();
+
+ ab.x = end.x - start.x;
+ ab.y = end.y - start.y;
+ ab.z = end.z - start.z;
+
+ ap.x = center.x - start.x;
+ ap.y = center.y - start.y;
+ ap.z = center.z - start.z;
+
+ abDotAp = ab.dot(ap);
+
+ if (abDotAp < 0.0)
+ return false; // line segment points away from sphere.
+
+ abLenSq = ab.lengthSquared();
+ acLenSq = abDotAp * abDotAp / abLenSq;
+
+ if (acLenSq < abLenSq)
+ return false; // C doesn't lies between end points of edge.
+
+ radiusSq = radius * radius;
+ apLenSq = ap.lengthSquared();
+
+ if ((apLenSq - acLenSq) <= radiusSq)
+ return true;
+
+ return false;
+ }
+
+
+ static double det2D(Point2d a, Point2d b, Point2d p) {
+ return (((p).x - (a).x) * ((a).y - (b).y) +
+ ((a).y - (p).y) * ((a).x - (b).x));
+ }
+
+ // Assume coord is CCW.
+ static boolean pointIntersectPolygon2D(Vector3d normal, Point3d[] coord,
+ Point3d point) {
+
+ double absNrmX, absNrmY, absNrmZ;
+ Point2d coord2D[] = new Point2d[coord.length];
+ Point2d pnt = new Point2d();
+
+ int i, j, axis;
+
+ // Project 3d points onto 2d plane.
+ // Note : Area of polygon is not preserve in this projection, but
+ // it doesn't matter here.
+
+ // Find the axis of projection.
+ absNrmX = Math.abs(normal.x);
+ absNrmY = Math.abs(normal.y);
+ absNrmZ = Math.abs(normal.z);
+
+ if (absNrmX > absNrmY)
+ axis = 0;
+ else
+ axis = 1;
+
+ if (axis == 0) {
+ if (absNrmX < absNrmZ)
+ axis = 2;
+ }
+ else if (axis == 1) {
+ if (absNrmY < absNrmZ)
+ axis = 2;
+ }
+
+ // System.out.println("Normal " + normal + " axis " + axis );
+
+ for (i=0; i<coord.length; i++) {
+ coord2D[i] = new Point2d();
+
+ switch (axis) {
+ case 0:
+ coord2D[i].x = coord[i].y;
+ coord2D[i].y = coord[i].z;
+ break;
+
+ case 1:
+ coord2D[i].x = coord[i].x;
+ coord2D[i].y = coord[i].z;
+ break;
+
+ case 2:
+ coord2D[i].x = coord[i].x;
+ coord2D[i].y = coord[i].y;
+ break;
+ }
+ // System.out.println("i " + i + " u " + uCoor[i] + " v " + vCoor[i]);
+ }
+
+ switch (axis) {
+ case 0:
+ pnt.x = point.y;
+ pnt.y = point.z;
+ break;
+
+ case 1:
+ pnt.x = point.x;
+ pnt.y = point.z;
+ break;
+
+ case 2:
+ pnt.x = point.x;
+ pnt.y = point.y;
+ break;
+ }
+
+ // Do determinant test.
+ for (j=0; j<coord.length; j++) {
+ if (j<(coord.length-1))
+ if (det2D(coord2D[j], coord2D[j+1], pnt)>0.0)
+ ;
+ else
+ return false;
+ else
+ if (det2D(coord2D[j], coord2D[0], pnt)>0.0)
+ ;
+ else
+ return false;
+ }
+ return true;
+ }
+
+
+ static boolean edgeIntersectPlane(Vector3d normal, Point3d pnt,
+ Point3d start, Point3d end, Point3d iPnt){
+
+ Vector3d tempV3d = new Vector3d();
+ Vector3d direction = new Vector3d();
+ double pD, pNrmDotrDir, tr;
+
+ // Compute plane D.
+ tempV3d.set((Tuple3d) pnt);
+ pD = normal.dot(tempV3d);
+
+ direction.x = end.x - start.x;
+ direction.y = end.y - start.y;
+ direction.z = end.z - start.z;
+
+ pNrmDotrDir = normal.dot(direction);
+
+ // edge is parallel to plane.
+ if (pNrmDotrDir== 0.0) {
+ // System.out.println("Edge is parallel to plane.");
+ return false;
+ }
+
+ tempV3d.set((Tuple3d) start);
+
+ tr = (pD - normal.dot(tempV3d))/ pNrmDotrDir;
+
+ // Edge intersects the plane behind the edge's start.
+ // or exceed the edge's length.
+ if ((tr < 0.0 ) || (tr > 1.0 )) {
+ // System.out.println("Edge intersects the plane behind the start or exceed end.");
+ return false;
+ }
+
+ iPnt.x = start.x + tr * direction.x;
+ iPnt.y = start.y + tr * direction.y;
+ iPnt.z = start.z + tr * direction.z;
+
+ return true;
+ }
+
+ // Assume coord is CCW.
+ static boolean edgeIntersectPolygon2D(Vector3d normal, Point3d[] coord,
+ Point3d[] seg) {
+
+ double absNrmX, absNrmY, absNrmZ;
+ Point2d coord2D[] = new Point2d[coord.length];
+ Point2d seg2D[] = new Point2d[2];
+
+ int i, j, axis;
+
+ // Project 3d points onto 2d plane.
+ // Note : Area of polygon is not preserve in this projection, but
+ // it doesn't matter here.
+
+ // Find the axis of projection.
+ absNrmX = Math.abs(normal.x);
+ absNrmY = Math.abs(normal.y);
+ absNrmZ = Math.abs(normal.z);
+
+ if (absNrmX > absNrmY)
+ axis = 0;
+ else
+ axis = 1;
+
+ if (axis == 0) {
+ if (absNrmX < absNrmZ)
+ axis = 2;
+ }
+ else if (axis == 1) {
+ if (absNrmY < absNrmZ)
+ axis = 2;
+ }
+
+ // System.out.println("Normal " + normal + " axis " + axis );
+
+ for (i=0; i<coord.length; i++) {
+ coord2D[i] = new Point2d();
+
+ switch (axis) {
+ case 0:
+ coord2D[i].x = coord[i].y;
+ coord2D[i].y = coord[i].z;
+ break;
+
+ case 1:
+ coord2D[i].x = coord[i].x;
+ coord2D[i].y = coord[i].z;
+ break;
+
+ case 2:
+ coord2D[i].x = coord[i].x;
+ coord2D[i].y = coord[i].y;
+ break;
+ }
+ // System.out.println("i " + i + " u " + uCoor[i] + " v " + vCoor[i]);
+ }
+
+ for (i=0; i<2; i++) {
+ seg2D[i] = new Point2d();
+ switch (axis) {
+ case 0:
+ seg2D[i].x = seg[i].y;
+ seg2D[i].y = seg[i].z;
+ break;
+
+ case 1:
+ seg2D[i].x = seg[i].x;
+ seg2D[i].y = seg[i].z;
+ break;
+
+ case 2:
+ seg2D[i].x = seg[i].x;
+ seg2D[i].y = seg[i].y;
+ break;
+ }
+ // System.out.println("i " + i + " u " + uSeg[i] + " v " + vSeg[i]);
+ }
+
+ // Do determinant test.
+ boolean pntTest[][] = new boolean[2][coord.length];
+ boolean testFlag;
+
+ for (j=0; j<coord.length; j++) {
+ for (i=0; i<2; i++) {
+ if (j<(coord.length-1))
+ pntTest[i][j] = (det2D(coord2D[j], coord2D[j+1], seg2D[i])<0.0);
+ else
+ pntTest[i][j] = (det2D(coord2D[j], coord2D[0], seg2D[i])<0.0);
+ }
+
+ if ((pntTest[0][j]==false) && (pntTest[1][j]==false))
+ return false;
+ }
+
+ testFlag = true;
+ for (i=0; i<coord.length; i++) {
+ if (pntTest[0][i]==false) {
+ testFlag = false;
+ break;
+ }
+ }
+
+ if (testFlag == true)
+ return true; // start point is inside polygon.
+
+ testFlag = true;
+ for (i=0; i<coord.length; i++) {
+ if (pntTest[1][i]==false) {
+ testFlag = false;
+ break;
+ }
+ }
+
+ if (testFlag == true)
+ return true; // end point is inside polygon.
+
+
+ int cnt = 0;
+ for (i=0; i<coord.length; i++) {
+ if (det2D(seg2D[0], seg2D[1], coord2D[i])<0.0)
+ cnt++;
+ }
+
+ if ((cnt==0)||(cnt==coord.length))
+ return false;
+
+ return true;
+ }
+
+ static boolean intersectPolygon(Point3d coord1[], Point3d coord2[],
+ boolean doTrivialTest) {
+ int i, j;
+ Vector3d vec0 = new Vector3d(); //Edge vector from point 0 to point 1;
+ Vector3d vec1 = new Vector3d(); //Edge vector from point 0 to point 2 or 3;
+ Vector3d pNrm = new Vector3d();
+ boolean epFlag;
+
+ // compute plane normal for coord1.
+ for (i=0; i<coord1.length-1;) {
+ vec0.x = coord1[i+1].x - coord1[i].x;
+ vec0.y = coord1[i+1].y - coord1[i].y;
+ vec0.z = coord1[i+1].z - coord1[i++].z;
+ if (vec0.length() > 0.0)
+ break;
+ }
+
+ for (j=i; j<coord1.length-1; j++) {
+ vec1.x = coord1[j+1].x - coord1[j].x;
+ vec1.y = coord1[j+1].y - coord1[j].y;
+ vec1.z = coord1[j+1].z - coord1[j].z;
+ if (vec1.length() > 0.0)
+ break;
+ }
+
+ if (j == (coord1.length-1)) {
+ // System.out.println("(1) Degenerated polygon.");
+ return false; // Degenerated polygon.
+ }
+
+ /*
+ for (i=0; i<coord1.length; i++)
+ System.out.println("coord1 P" + i + " " + coord1[i]);
+ for (i=0; i<coord2.length; i++)
+ System.out.println("coord2 P" + i + " " + coord2[i]);
+ */
+
+ pNrm.cross(vec0,vec1);
+
+ if (pNrm.length() == 0.0) {
+ // System.out.println("(2) Degenerated polygon.");
+ return false; // Degenerated polygon.
+ }
+
+ // Do trivial test here.
+ if ( doTrivialTest == true) {
+ // Not implemented yet.
+ }
+
+ j = 0;
+ Point3d seg[] = new Point3d[2];
+ seg[0] = new Point3d();
+ seg[1] = new Point3d();
+
+ for (i=0; i<coord2.length; i++) {
+ if (i < (coord2.length-1))
+ epFlag = edgeIntersectPlane(pNrm, coord1[0], coord2[i],
+ coord2[i+1], seg[j]);
+ else
+ epFlag = edgeIntersectPlane(pNrm, coord1[0], coord2[i],
+ coord2[0], seg[j]);
+ if (epFlag == true) {
+ j++;
+ if (j>1)
+ break;
+ }
+ }
+
+ if (j==0)
+ return false;
+
+ if (coord2.length < 3)
+ return pointIntersectPolygon2D(pNrm, coord1, seg[0]);
+
+ return edgeIntersectPolygon2D(pNrm, coord1, seg);
+ }
+
+
+ static final boolean isNonZero(double v) {
+ return ((v > EPS) || (v < -EPS));
+
+ }
+
+
+ static boolean intersectRay(Point3d coordinates[],
+ PickRay ray, PickIntersection pi) {
+ Point3d origin = getPoint3d();
+ Vector3d direction = getVector3d();
+ boolean result;
+ ray.get (origin, direction);
+ result = intersectRayOrSegment(coordinates, direction, origin, pi, false);
+ freeVector3d(direction);
+ freePoint3d(origin);
+ return result;
+ }
+
+ /**
+ * Return true if triangle or quad intersects with ray and the distance is
+ * stored in pr.
+ * */
+ static boolean intersectRayOrSegment(Point3d coordinates[],
+ Vector3d direction, Point3d origin,
+ PickIntersection pi, boolean isSegment) {
+ Vector3d vec0, vec1, pNrm, tempV3d;
+ Point3d iPnt;
+
+ vec0 = getVector3d();
+ vec1 = getVector3d();
+ pNrm = getVector3d();
+
+ double absNrmX, absNrmY, absNrmZ, pD = 0.0;
+ double pNrmDotrDir = 0.0;
+
+ boolean isIntersect = false;
+ int i, j, k=0, l = 0;
+
+ // Compute plane normal.
+ for (i=0; i<coordinates.length; i++) {
+ if (i != coordinates.length-1) {
+ l = i+1;
+ } else {
+ l = 0;
+ }
+ vec0.x = coordinates[l].x - coordinates[i].x;
+ vec0.y = coordinates[l].y - coordinates[i].y;
+ vec0.z = coordinates[l].z - coordinates[i].z;
+ if (vec0.length() > 0.0) {
+ break;
+ }
+ }
+
+
+ for (j=l; j<coordinates.length; j++) {
+ if (j != coordinates.length-1) {
+ k = j+1;
+ } else {
+ k = 0;
+ }
+ vec1.x = coordinates[k].x - coordinates[j].x;
+ vec1.y = coordinates[k].y - coordinates[j].y;
+ vec1.z = coordinates[k].z - coordinates[j].z;
+ if (vec1.length() > 0.0) {
+ break;
+ }
+ }
+
+ pNrm.cross(vec0,vec1);
+
+ if ((vec1.length() == 0) || (pNrm.length() == 0)) {
+ // degenerated to line if vec0.length() == 0
+ // or vec0.length > 0 and vec0 parallel to vec1
+ k = (l == 0 ? coordinates.length-1: l-1);
+ isIntersect = intersectLineAndRay(coordinates[l],
+ coordinates[k],
+ origin,
+ direction,
+ pi);
+
+ // put the Vectors on the freelist
+ freeVector3d(vec0);
+ freeVector3d(vec1);
+ freeVector3d(pNrm);
+ return isIntersect;
+ }
+
+ // It is possible that Quad is degenerate to Triangle
+ // at this point
+
+ pNrmDotrDir = pNrm.dot(direction);
+
+ // Ray is parallel to plane.
+ if (pNrmDotrDir == 0.0) {
+ // Ray is parallel to plane
+ // Check line/triangle intersection on plane.
+ for (i=0; i < coordinates.length ;i++) {
+ if (i != coordinates.length-1) {
+ k = i+1;
+ } else {
+ k = 0;
+ }
+ if (intersectLineAndRay(coordinates[i],
+ coordinates[k],
+ origin,
+ direction,
+ pi)) {
+ isIntersect = true;
+ break;
+ }
+ }
+ freeVector3d(vec0);
+ freeVector3d(vec1);
+ freeVector3d(pNrm);
+ return isIntersect;
+ }
+
+ // Plane equation: (p - p0)*pNrm = 0 or p*pNrm = pD;
+ tempV3d = getVector3d();
+ tempV3d.set((Tuple3d) coordinates[0]);
+ pD = pNrm.dot(tempV3d);
+ tempV3d.set((Tuple3d) origin);
+
+ // Substitute Ray equation:
+ // p = origin + pi.distance*direction
+ // into the above Plane equation
+
+ double dist = (pD - pNrm.dot(tempV3d))/ pNrmDotrDir;
+
+ // Ray intersects the plane behind the ray's origin.
+ if ((dist < -EPS ) ||
+ (isSegment && (dist > 1.0+EPS))) {
+ // Ray intersects the plane behind the ray's origin
+ // or intersect point not fall in Segment
+ freeVector3d(vec0);
+ freeVector3d(vec1);
+ freeVector3d(pNrm);
+ freeVector3d(tempV3d);
+ return false;
+ }
+
+ // Now, one thing for sure the ray intersect the plane.
+ // Find the intersection point.
+ iPnt = getPoint3d();
+ iPnt.x = origin.x + direction.x * dist;
+ iPnt.y = origin.y + direction.y * dist;
+ iPnt.z = origin.z + direction.z * dist;
+
+ // Project 3d points onto 2d plane.
+ // Find the axis so that area of projection is maximize.
+ absNrmX = Math.abs(pNrm.x);
+ absNrmY = Math.abs(pNrm.y);
+ absNrmZ = Math.abs(pNrm.z);
+
+ // Check out
+ // http://astronomy.swin.edu.au/~pbourke/geometry/insidepoly/
+ // Solution 3:
+ // All sign of (y - y0) (x1 - x0) - (x - x0) (y1 - y0)
+ // must agree.
+ double sign, t, lastSign = 0;
+ Point3d p0 = coordinates[coordinates.length-1];
+ Point3d p1 = coordinates[0];
+
+ isIntersect = true;
+
+ if (absNrmX > absNrmY) {
+ if (absNrmX < absNrmZ) {
+ for (i=0; i < coordinates.length; i++) {
+ p0 = coordinates[i];
+ p1 = (i != coordinates.length-1 ? coordinates[i+1]: coordinates[0]);
+ sign = (iPnt.y - p0.y)*(p1.x - p0.x) -
+ (iPnt.x - p0.x)*(p1.y - p0.y);
+ if (isNonZero(sign)) {
+ if (sign*lastSign < 0) {
+ isIntersect = false;
+ break;
+ }
+ lastSign = sign;
+ } else { // point on line, check inside interval
+ t = p1.y - p0.y;
+ if (isNonZero(t)) {
+ t = (iPnt.y - p0.y)/t;
+ isIntersect = ((t > -EPS) && (t < 1+EPS));
+ break;
+ } else {
+ t = p1.x - p0.x;
+ if (isNonZero(t)) {
+ t = (iPnt.x - p0.x)/t;
+ isIntersect = ((t > -EPS) && (t < 1+EPS));
+ break;
+ } else {
+ lastSign = 0; //degenerate line=>point
+ }
+ }
+ }
+ }
+ } else {
+ for (i=0; i<coordinates.length; i++) {
+ p0 = coordinates[i];
+ p1 = (i != coordinates.length-1 ? coordinates[i+1]: coordinates[0]);
+ sign = (iPnt.y - p0.y)*(p1.z - p0.z) -
+ (iPnt.z - p0.z)*(p1.y - p0.y);
+ if (isNonZero(sign)) {
+ if (sign*lastSign < 0) {
+ isIntersect = false;
+ break;
+ }
+ lastSign = sign;
+ } else { // point on line, check inside interval
+ t = p1.y - p0.y;
+
+ if (isNonZero(t)) {
+ t = (iPnt.y - p0.y)/t;
+ isIntersect = ((t > -EPS) && (t < 1+EPS));
+ break;
+
+ } else {
+ t = p1.z - p0.z;
+ if (isNonZero(t)) {
+ t = (iPnt.z - p0.z)/t;
+ isIntersect = ((t > -EPS) && (t < 1+EPS));
+ break;
+ } else {
+ lastSign = 0; //degenerate line=>point
+ }
+ }
+ }
+ }
+ }
+ } else {
+ if (absNrmY < absNrmZ) {
+ for (i=0; i<coordinates.length; i++) {
+ p0 = coordinates[i];
+ p1 = (i != coordinates.length-1 ? coordinates[i+1]: coordinates[0]);
+ sign = (iPnt.y - p0.y)*(p1.x - p0.x) -
+ (iPnt.x - p0.x)*(p1.y - p0.y);
+ if (isNonZero(sign)) {
+ if (sign*lastSign < 0) {
+ isIntersect = false;
+ break;
+ }
+ lastSign = sign;
+ } else { // point on line, check inside interval
+ t = p1.y - p0.y;
+ if (isNonZero(t)) {
+ t = (iPnt.y - p0.y)/t;
+ isIntersect = ((t > -EPS) && (t < 1+EPS));
+ break;
+ } else {
+ t = p1.x - p0.x;
+ if (isNonZero(t)) {
+ t = (iPnt.x - p0.x)/t;
+ isIntersect = ((t > -EPS) && (t < 1+EPS));
+ break;
+ } else {
+ lastSign = 0; //degenerate line=>point
+ }
+ }
+ }
+ }
+ } else {
+ for (i=0; i<coordinates.length; i++) {
+ p0 = coordinates[i];
+ p1 = (i != coordinates.length-1 ? coordinates[i+1]: coordinates[0]);
+ sign = (iPnt.x - p0.x)*(p1.z - p0.z) -
+ (iPnt.z - p0.z)*(p1.x - p0.x);
+ if (isNonZero(sign)) {
+ if (sign*lastSign < 0) {
+ isIntersect = false;
+ break;
+ }
+ lastSign = sign;
+ } else { // point on line, check inside interval
+ t = p1.x - p0.x;
+ if (isNonZero(t)) {
+ t = (iPnt.x - p0.x)/t;
+ isIntersect = ((t > -EPS) && (t < 1+EPS));
+ break;
+ } else {
+ t = p1.z - p0.z;
+ if (isNonZero(t)) {
+ t = (iPnt.z - p0.z)/t;
+ isIntersect = ((t > -EPS) && (t < 1+EPS));
+ break;
+ } else {
+ lastSign = 0; //degenerate line=>point
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (isIntersect) {
+ pi.setDistance(dist*direction.length());
+ pi.setPointCoordinatesVW(iPnt);
+ }
+ freeVector3d(vec0);
+ freeVector3d(vec1);
+ freeVector3d(pNrm);
+ freeVector3d(tempV3d);
+ freePoint3d(iPnt);
+ return isIntersect;
+ }
+
+
+ /**
+ Return true if triangle or quad intersects with segment and the distance is
+ stored in dist.
+ */
+ static boolean intersectSegment (Point3d coordinates[], PickSegment segment,
+ PickIntersection pi) {
+ Point3d start = getPoint3d();
+ Point3d end = getPoint3d();
+ Vector3d direction = getVector3d();
+ boolean result;
+ segment.get(start, end);
+ direction.x = end.x - start.x;
+ direction.y = end.y - start.y;
+ direction.z = end.z - start.z;
+ result = intersectRayOrSegment(coordinates, direction, start, pi, true);
+ freeVector3d(direction);
+ freePoint3d(start);
+ freePoint3d(end);
+ return result;
+ }
+
+
+ /**
+ Return true if point is on the inside of halfspace test. The halfspace is
+ partition by the plane of triangle or quad.
+ */
+
+ static boolean inside (Point3d coordinates[], PickPoint point, int ccw) {
+
+ Vector3d vec0 = new Vector3d(); //Edge vector from point 0 to point 1;
+ Vector3d vec1 = new Vector3d(); //Edge vector from point 0 to point 2 or 3;
+ Vector3d pNrm = new Vector3d();
+ double absNrmX, absNrmY, absNrmZ, pD = 0.0;
+ Vector3d tempV3d = new Vector3d();
+ double pNrmDotrDir = 0.0;
+
+ double tempD;
+
+ int i, j;
+
+ Point3d location = new Point3d ();
+ point.get (location);
+
+ // Compute plane normal.
+ for (i=0; i<coordinates.length-1;) {
+ vec0.x = coordinates[i+1].x - coordinates[i].x;
+ vec0.y = coordinates[i+1].y - coordinates[i].y;
+ vec0.z = coordinates[i+1].z - coordinates[i++].z;
+ if (vec0.length() > 0.0)
+ break;
+ }
+
+ for (j=i; j<coordinates.length-1; j++) {
+ vec1.x = coordinates[j+1].x - coordinates[j].x;
+ vec1.y = coordinates[j+1].y - coordinates[j].y;
+ vec1.z = coordinates[j+1].z - coordinates[j].z;
+ if (vec1.length() > 0.0)
+ break;
+ }
+
+ if (j == (coordinates.length-1)) {
+ // System.out.println("(1) Degenerated polygon.");
+ return false; // Degenerated polygon.
+ }
+
+ /*
+ System.out.println("Ray orgin : " + origin + " dir " + direction);
+ System.out.println("Triangle/Quad :");
+ for (i=0; i<coordinates.length; i++)
+ System.out.println("P" + i + " " + coordinates[i]);
+ */
+
+ if ( ccw == 0x1)
+ pNrm.cross(vec0,vec1);
+ else
+ pNrm.cross(vec1,vec0);
+
+ if (pNrm.length() == 0.0) {
+ // System.out.println("(2) Degenerated polygon.");
+ return false; // Degenerated polygon.
+ }
+ // Compute plane D.
+ tempV3d.set((Tuple3d) coordinates[0]);
+ pD = pNrm.dot(tempV3d);
+
+ tempV3d.set((Tuple3d) location);
+
+ if ((pD - pNrm.dot(tempV3d)) > 0.0 ) {
+ // System.out.println("point is on the outside of plane.");
+ return false;
+ }
+ else
+ return true;
+ }
+
+ static boolean intersectPntAndPnt (Point3d pnt1, Point3d pnt2,
+ PickIntersection pi) {
+
+ if ((pnt1.x == pnt2.x) && (pnt1.y == pnt2.y) && (pnt1.z == pnt2.z)) {
+ pi.setPointCoordinatesVW (pnt1);
+ pi.setDistance (0.0);
+ return true;
+ }
+ else
+ return false;
+ }
+
+ static boolean intersectPntAndRay (Point3d pnt, Point3d ori, Vector3d dir,
+ PickIntersection pi) {
+ int flag = 0;
+ double temp;
+ double dist;
+
+ if (dir.x != 0.0) {
+ flag = 0;
+ dist = (pnt.x - ori.x)/dir.x;
+ }
+ else if (dir.y != 0.0) {
+ if (pnt.x != ori.x)
+ return false;
+ flag = 1;
+ dist = (pnt.y - ori.y)/dir.y;
+ }
+ else if (dir.z != 0.0) {
+ if ((pnt.x != ori.x)||(pnt.y != ori.y))
+ return false;
+ flag = 2;
+ dist = (pnt.z - ori.z)/dir.z;
+
+ }
+ else
+ return false;
+
+ if (dist < 0.0)
+ return false;
+
+ if (flag == 0) {
+ temp = ori.y + dist * dir.y;
+ if ((pnt.y < (temp - Double.MIN_VALUE)) || (pnt.y > (temp + Double.MIN_VALUE)))
+ return false;
+ }
+
+ if (flag < 2) {
+ temp = ori.z + dist * dir.z;
+ if ((pnt.z < (temp - Double.MIN_VALUE)) || (pnt.z > (temp + Double.MIN_VALUE)))
+ return false;
+ }
+
+ pi.setPointCoordinatesVW (pnt);
+ pi.setDistance (dist);
+
+ return true;
+
+ }
+
+ static boolean intersectLineAndRay(Point3d start, Point3d end,
+ Point3d ori, Vector3d dir,
+ PickIntersection pi) {
+
+ double m00, m01, m10, m11;
+ double mInv00, mInv01, mInv10, mInv11;
+ double dmt, t, s, tmp1, tmp2;
+ Vector3d lDir;
+ double dist;
+
+ // System.out.println("Intersect : intersectLineAndRay");
+ // System.out.println("start " + start + " end " + end );
+ // System.out.println("ori " + ori + " dir " + dir);
+
+ lDir = new Vector3d(end.x - start.x, end.y - start.y,
+ end.z - start.z);
+
+ m00 = lDir.x;
+ m01 = -dir.x;
+ m10 = lDir.y;
+ m11 = -dir.y;
+
+ // Get the determinant.
+ dmt = (m00 * m11) - (m10 * m01);
+
+ if (dmt==0.0) { // No solution, hence no intersect.
+ // System.out.println("dmt is zero");
+ boolean isIntersect = false;
+ if ((lDir.x == 0) && (lDir.y == 0) && (lDir.z == 0)) {
+ isIntersect = intersectPntAndRay(start, ori, dir, pi);
+ if (isIntersect) {
+ pi.setPointCoordinatesVW(start);
+ pi.setDistance(0);
+ }
+ }
+ freeVector3d(lDir);
+ return isIntersect;
+ }
+ // Find the inverse.
+ tmp1 = 1/dmt;
+
+ mInv00 = tmp1 * m11;
+ mInv01 = tmp1 * (-m01);
+ mInv10 = tmp1 * (-m10);
+ mInv11 = tmp1 * m00;
+
+ tmp1 = ori.x - start.x;
+ tmp2 = ori.y - start.y;
+
+ t = mInv00 * tmp1 + mInv01 * tmp2;
+ s = mInv10 * tmp1 + mInv11 * tmp2;
+
+ if (s<0.0) { // Before the origin of ray.
+ // System.out.println("Before the origin of ray " + s);
+ return false;
+ }
+ if ((t<0)||(t>1.0)) {// Before or after the end points of line.
+ // System.out.println("Before or after the end points of line. " + t);
+ return false;
+ }
+
+ tmp1 = ori.z + s * dir.z;
+ tmp2 = start.z + t * lDir.z;
+
+ if ((tmp1 < (tmp2 - Double.MIN_VALUE)) ||
+ (tmp1 > (tmp2 + Double.MIN_VALUE))) {
+ // System.out.println("No intersection : tmp1 " + tmp1 + " tmp2 " + tmp2);
+ return false;
+ }
+ dist = s;
+
+ pi.setDistance (dist);
+ Point3d iPnt = new Point3d ();
+ iPnt.scaleAdd (s, dir, ori);
+ pi.setPointCoordinatesVW (iPnt);
+
+ // System.out.println("Intersected : tmp1 " + tmp1 + " tmp2 " + tmp2);
+ return true;
+ }
+
+ /**
+ Return true if triangle or quad intersects with cylinder and the
+ distance is stored in pr.
+ */
+ static boolean intersectCylinder (Point3d coordinates[],
+ PickCylinder cyl, PickIntersection pi) {
+
+ Point3d origin = getPoint3d();
+ Point3d end = getPoint3d();
+ Vector3d direction = getVector3d();
+ Point3d iPnt1 = getPoint3d();
+ Point3d iPnt2 = getPoint3d();
+ Vector3d originToIpnt = getVector3d();
+
+ // Get cylinder information
+ cyl.getOrigin (origin);
+ cyl.getDirection (direction);
+ double radius = cyl.getRadius ();
+
+ if (cyl instanceof PickCylinderSegment) {
+ ((PickCylinderSegment)cyl).getEnd (end);
+ }
+
+ // If the ray intersects, we're good (do not do this if we only have
+ // a segment
+ if (coordinates.length > 2) {
+ if (cyl instanceof PickCylinderRay) {
+ if (intersectRay (coordinates, new PickRay (origin, direction), pi)) {
+ freePoint3d(origin);
+ freePoint3d(end);
+ freeVector3d(direction);
+ freePoint3d(iPnt1);
+ freePoint3d(iPnt2);
+ freeVector3d(originToIpnt);
+
+ return true;
+ }
+ }
+ else {
+ if (intersectSegment (coordinates, new PickSegment (origin, end), pi)) {
+ freePoint3d(origin);
+ freePoint3d(end);
+ freeVector3d(direction);
+ freePoint3d(iPnt1);
+ freePoint3d(iPnt2);
+ freeVector3d(originToIpnt);
+
+ return true;
+ }
+ }
+ }
+
+ // Ray doesn't intersect, check distance to edges
+ double sqDistToEdge;
+ for (int i=0; i<coordinates.length-1;i++) {
+ if (cyl instanceof PickCylinderSegment) {
+ sqDistToEdge =
+ Distance.segmentToSegment (origin, end,
+ coordinates[i], coordinates[i+1],
+ iPnt1, iPnt2, null);
+ }
+ else {
+ sqDistToEdge =
+ Distance.rayToSegment (origin, direction,
+ coordinates[i], coordinates[i+1],
+ iPnt1, iPnt2, null);
+ }
+ if (sqDistToEdge <= radius*radius) {
+ pi.setPointCoordinatesVW (iPnt2);
+ originToIpnt.sub (iPnt1, origin);
+ pi.setDistance (originToIpnt.length());
+
+ freePoint3d(origin);
+ freePoint3d(end);
+ freeVector3d(direction);
+ freePoint3d(iPnt1);
+ freePoint3d(iPnt2);
+ freeVector3d(originToIpnt);
+
+ return true;
+ }
+ }
+
+ freePoint3d(origin);
+ freePoint3d(end);
+ freeVector3d(direction);
+ freePoint3d(iPnt1);
+ freePoint3d(iPnt2);
+ freeVector3d(originToIpnt);
+
+ return false;
+ }
+
+ /**
+ Return true if triangle or quad intersects with cone. The
+ distance is stored in pr.
+ */
+ static boolean intersectCone (Point3d coordinates[],
+ PickCone cone, PickIntersection pi) {
+
+ Point3d origin = getPoint3d();
+ Point3d end = getPoint3d();
+ Vector3d direction = getVector3d();
+ Vector3d originToIpnt = getVector3d();
+ double distance;
+
+ Point3d iPnt1 = getPoint3d();
+ Point3d iPnt2 = getPoint3d();
+ Vector3d vector = getVector3d();
+
+ // Get cone information
+ cone.getOrigin (origin);
+ cone.getDirection (direction);
+ double radius;
+
+ if (cone instanceof PickConeSegment) {
+ ((PickConeSegment)cone).getEnd (end);
+ }
+
+ // If the ray intersects, we're good (do not do this if we only have
+ // a segment
+ if (coordinates.length > 2) {
+ if (cone instanceof PickConeRay) {
+ if (intersectRay (coordinates, new PickRay (origin, direction), pi)) {
+ freePoint3d(origin);
+ freePoint3d(end);
+ freePoint3d(iPnt1);
+ freePoint3d(iPnt2);
+ freeVector3d(direction);
+ freeVector3d(originToIpnt);
+ freeVector3d(vector);
+ return true;
+ }
+ }
+ else {
+ if (intersectSegment (coordinates, new PickSegment (origin, end),
+ pi)) {
+ freePoint3d(origin);
+ freePoint3d(end);
+ freePoint3d(iPnt1);
+ freePoint3d(iPnt2);
+ freeVector3d(direction);
+ freeVector3d(originToIpnt);
+ freeVector3d(vector);
+ return true;
+ }
+ }
+ }
+
+ // Ray doesn't intersect, check distance to edges
+ double sqDistToEdge;
+ for (int i=0; i<coordinates.length-1;i++) {
+ if (cone instanceof PickConeSegment) {
+ sqDistToEdge =
+ Distance.segmentToSegment (origin, end,
+ coordinates[i], coordinates[i+1],
+ iPnt1, iPnt2, null);
+ }
+ else {
+ sqDistToEdge =
+ Distance.rayToSegment (origin, direction,
+ coordinates[i], coordinates[i+1],
+ iPnt1, iPnt2, null);
+ }
+ originToIpnt.sub (iPnt1, origin);
+ distance = originToIpnt.length();
+ radius = Math.tan (cone.getSpreadAngle()) * distance;
+ if (sqDistToEdge <= radius*radius) {
+ // System.out.println ("intersectCone: edge "+i+" intersected");
+ pi.setPointCoordinatesVW (iPnt2);
+ pi.setDistance (distance);
+
+ freePoint3d(origin);
+ freePoint3d(end);
+ freePoint3d(iPnt1);
+ freePoint3d(iPnt2);
+ freeVector3d(direction);
+ freeVector3d(originToIpnt);
+ freeVector3d(vector);
+
+ return true;
+ }
+ }
+
+ freePoint3d(origin);
+ freePoint3d(end);
+ freePoint3d(iPnt1);
+ freePoint3d(iPnt2);
+ freeVector3d(direction);
+ freeVector3d(originToIpnt);
+ freeVector3d(vector);
+
+ return false;
+ }
+
+
+ /**
+ Return true if point intersects with cylinder and the
+ distance is stored in pi.
+ */
+ static boolean intersectCylinder (Point3d pt,
+ PickCylinder cyl, PickIntersection pi) {
+
+ Point3d origin = getPoint3d();
+ Point3d end = getPoint3d();
+ Vector3d direction = getVector3d();
+ Point3d iPnt = getPoint3d();
+ Vector3d originToIpnt = getVector3d();
+
+ // Get cylinder information
+ cyl.getOrigin (origin);
+ cyl.getDirection (direction);
+ double radius = cyl.getRadius ();
+ double sqDist;
+
+ if (cyl instanceof PickCylinderSegment) {
+ ((PickCylinderSegment)cyl).getEnd (end);
+ sqDist = Distance.pointToSegment (pt, origin, end, iPnt, null);
+ }
+ else {
+ sqDist = Distance.pointToRay (pt, origin, direction, iPnt, null);
+ }
+ if (sqDist <= radius*radius) {
+ pi.setPointCoordinatesVW (pt);
+ originToIpnt.sub (iPnt, origin);
+ pi.setDistance (originToIpnt.length());
+
+ freePoint3d(origin);
+ freePoint3d(end);
+ freeVector3d(direction);
+ freePoint3d(iPnt);
+ freeVector3d(originToIpnt);
+ return true;
+ }
+ freePoint3d(origin);
+ freePoint3d(end);
+ freeVector3d(direction);
+ freePoint3d(iPnt);
+ freeVector3d(originToIpnt);
+ return false;
+ }
+ /**
+ Return true if point intersects with cone and the
+ distance is stored in pi.
+ */
+ static boolean intersectCone (Point3d pt,
+ PickCone cone, PickIntersection pi) {
+
+ // System.out.println ("Intersect.intersectCone point");
+
+ Point3d origin = getPoint3d();
+ Point3d end = getPoint3d();
+ Vector3d direction = getVector3d();
+ Point3d iPnt = getPoint3d();// the closest point on the cone vector
+ Vector3d originToIpnt = getVector3d();
+
+ // Get cone information
+ cone.getOrigin (origin);
+ cone.getDirection (direction);
+ double radius;
+ double distance;
+ double sqDist;
+
+ if (cone instanceof PickConeSegment) {
+ ((PickConeSegment)cone).getEnd (end);
+ sqDist = Distance.pointToSegment (pt, origin, end, iPnt, null);
+ }
+ else {
+ sqDist = Distance.pointToRay (pt, origin, direction, iPnt, null);
+ }
+ originToIpnt.sub (iPnt, origin);
+ distance = originToIpnt.length();
+ radius = Math.tan (cone.getSpreadAngle()) * distance;
+ if (sqDist <= radius*radius) {
+ pi.setPointCoordinatesVW (pt);
+ pi.setDistance (distance);
+
+ freePoint3d(origin);
+ freePoint3d(end);
+ freeVector3d(direction);
+ freePoint3d(iPnt);
+ freeVector3d(originToIpnt);
+
+ return true;
+ }
+ freePoint3d(origin);
+ freePoint3d(end);
+ freeVector3d(direction);
+ freePoint3d(iPnt);
+ freeVector3d(originToIpnt);
+
+ return false;
+ }
+
+ static Vector3d getVector3d() {
+ return (Vector3d)UtilFreelistManager.vector3dFreelist.getObject();
+ }
+
+ static void freeVector3d(Vector3d v) {
+ UtilFreelistManager.vector3dFreelist.add(v);
+ }
+
+ static Point3d getPoint3d() {
+ return (Point3d)UtilFreelistManager.point3dFreelist.getObject();
+ }
+
+ static void freePoint3d(Point3d p) {
+ UtilFreelistManager.point3dFreelist.add(p);
+ }
+
+
+} // PickResult
diff --git a/src/classes/share/com/sun/j3d/utils/picking/PickTool.java b/src/classes/share/com/sun/j3d/utils/picking/PickTool.java
new file mode 100644
index 0000000..61ae9db
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/picking/PickTool.java
@@ -0,0 +1,1027 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.picking;
+
+import javax.vecmath.*;
+import javax.media.j3d.*;
+import com.sun.j3d.internal.*;
+
+/**
+ * The base class for picking operations.
+ * The picking methods will return a PickResult object for each object picked,
+ * which can then be queried to
+ * obtain more detailed information about the specific objects that were
+ * picked.
+ * <p>
+ * The pick mode specifies the detail level of picking before the PickResult
+ * is returned:
+ * <p>
+ * <UL>
+ * <LI> PickTool.BOUNDS - Pick using the bounds of the pickable nodes. The
+ * PickResult returned will contain the SceneGraphPath to the picked Node.
+ * </LI>
+ * <LI> PickTool.GEOMETRY will pick using the geometry of the pickable nodes.
+ * The PickResult returned will contain the SceneGraphPath to the picked Node.
+ * Geometry nodes in the scene must have the ALLOW_INTERSECT capability set for
+ * this mode.</LI>
+ * <LI> PickTool.GEOMETRY_INTERSECT_INFO -is the same as GEOMETRY, but the
+ * the PickResult will also include information on each intersection
+ * of the pick shape with the geometry. The intersection information includes
+ * the sub-primitive picked (that is, the point, line, triangle or quad),
+ * the closest vertex to the center of the pick shape, and
+ * the intersection's coordinate, normal, color and texture coordinates.
+ * To allow this information to be generated, Shape3D and Morph nodes must have
+ * the ALLOW_GEOMETRY_READ capability set and GeometryArrays must have the
+ * ALLOW_FORMAT_READ,
+ * ALLOW_COUNT_READ, and ALLOW_COORDINATE_READ capabilities set, plus the
+ * ALLOW_COORDINATE_INDEX_READ capability for indexed geometry.
+ * To inquire
+ * the intersection color, normal or texture coordinates
+ * the corresponding READ capability bits must be set on the GeometryArray.
+ * </LI>
+ * </UL>
+ * <p> The utility method
+ * <A HREF="PickTool.html#setCapabilities(javax.media.j3d.Node, int)">
+ * <code>PickTool.setCapabilities(Node, int)</code></A>
+ * can be used before the scene graph is
+ * made live to set the
+ * capabilities of Shape3D, Morph or Geometry
+ * nodes to allow picking.
+ * <p>
+ * A PickResult from a lower level of detail pick can be used to
+ * inquire more detailed information if the capibility bits are set.
+ * This can be used to filter the PickResults
+ * before the more computationally intensive intersection processing.
+ * For example,
+ * the application can do a BOUNDS pick and then selectively inquire
+ * intersections on some of the PickResults. This will save the effort of
+ * doing intersection computation on the other PickResults.
+ * However, inquiring the intersections from a GEOMETRY pick will make
+ * the intersection computation happen twice, use GEOMETRY_INTERSECT_INFO
+ * if you want to inquire the intersection information on all the PickResults.
+ * <p>
+ * When using pickAllSorted or pickClosest methods, the picks
+ * will be sorted by the distance from the start point of the pick shape to
+ * the intersection point.
+ * <p>
+ * Morph nodes cannot be picked using the displayed geometry in
+ * GEOMETRY_INTERSECT_INFO mode due to limitations in the current Java3D core
+ * API (the current
+ * geometry of the the Morph cannot be inquired). Instead they are picked
+ * using
+ * the geometry at index 0 in the Morph, this limitation may be eliminated in a
+ * future release of Java3D.
+ * <p>
+ * If the pick shape is a PickBounds, the pick result will contain only the
+ * scene graph path, even if the mode is GEOMETRY_INTERSECT_INFO.
+ */
+public class PickTool {
+
+ /* OPEN ISSUES:
+ -- pickClosest() and pickAllSorted() using GEOMETRY and a non-PickRay
+ shape => unsorted picking.
+ -- Need to implement Morph geometry index 0 picking.
+ */
+
+ private final boolean debug = true;
+ protected boolean userDefineShape = false;
+
+ PickShape pickShape;
+
+ /** Used to store the BranchGroup used for picking */
+ BranchGroup pickRootBG = null;
+ /** Used to store the Locale used for picking */
+ Locale pickRootL = null;
+
+ /** Used to store a reference point used in determining how "close" points
+ are.
+ */
+ Point3d start = null;
+
+ /* pick mode, one of BOUNDS, GEOMETRY, etc. */
+ int mode = BOUNDS;
+
+ /** Use this mode to pick by bounds and get basic information
+ on the pick.
+ */
+ public static final int BOUNDS = 0x200;
+
+ /** Use this mode to pick by geometry and get basic
+ information on the pick.
+ */
+ public static final int GEOMETRY = 0x100;
+
+ /** Use this mode to pick by geometry and save
+ information about the intersections (intersected primitive,
+ intersection point and closest vertex).
+ */
+ public static final int GEOMETRY_INTERSECT_INFO = 0x400;
+
+
+ // Flags for the setCapabilities() method
+ /**
+ * Flag to pass to <CODE>setCapabilities(Node, int)<code> to set
+ * the Node's capabilities to allow intersection tests, but not
+ * inquire information about the intersections (use for GEOMETRY mode).
+ * @see PickTool#setCapabilities
+ */
+ public static final int INTERSECT_TEST = 0x1001;
+
+ /**
+ * Flag to pass to <CODE>setCapabilities(Node, int)<code> to set
+ * the Node's capabilities to allow inquiry of the intersection
+ * coordinate information.
+ * @see PickTool#setCapabilities
+ */
+ public static final int INTERSECT_COORD = 0x1002;
+
+ /**
+ * Flag to pass to <CODE>setCapabilities(Node, int)<code> to set
+ * the Node's capabilities to allow inquiry of all intersection
+ * information.
+ * @see PickTool#setCapabilities
+ */
+ public static final int INTERSECT_FULL = 0x1004;
+
+ /* ============================ METHODS ============================ */
+
+ /**
+ * Constructor with BranchGroup to be picked.
+ */
+ public PickTool (BranchGroup b) {
+ pickRootBG = b;
+ }
+
+ /** Returns the BranchGroup to be picked if the tool was initialized
+ with a BranchGroup, null otherwise.
+ */
+ public BranchGroup getBranchGroup() {
+ return pickRootBG;
+ }
+
+ /**
+ * Constructor with the Locale to be picked.
+ */
+ public PickTool (Locale l) {
+ pickRootL = l;
+ }
+
+ /**
+ * Returns the Locale to be picked if the tool was initialized with
+ * a Locale, null otherwise.
+ */
+ public Locale setBranchGroup (Locale l) {
+ return pickRootL;
+ }
+
+ /**
+ * Sets the capabilities on the Node and it's components to allow
+ * picking at the specified detail level.
+ * <p>
+ * Note that by default all com.sun.j3d.utils.geometry.Primitive
+ * objects with the same parameters share their geometry (e.g.,
+ * you can have 50 spheres in your scene, but the geometry is
+ * stored only once). Therefore the capabilities of the geometry
+ * are also shared, and once a shared node is live, the
+ * capabilities cannot be changed. To assign capabilities to
+ * Primitives with the same parameters, either set the
+ * capabilities before the primitive is set live, or specify the
+ * Primitive.GEOMETRY_NOT_SHARED constructor parameter when
+ * creating the primitive.
+ * @param node The node to modify
+ * @param level The capability level, must be one of INTERSECT_TEST,
+ * INTERSECT_COORD or INTERSECT_FULL
+ * @throws IllegalArgumentException if Node is not a Shape3D or Morph or
+ * if the flag value is not valid.
+ * @throws javax.media.j3d.RestrictedAccessException if the node is part
+ * of a live or compiled scene graph. */
+ static public void setCapabilities(Node node, int level) {
+ if (node instanceof Morph) {
+ Morph morph = (Morph) node;
+ switch (level) {
+ case INTERSECT_FULL:
+ /* intentional fallthrough */
+ case INTERSECT_COORD:
+ morph.setCapability(Morph.ALLOW_GEOMETRY_ARRAY_READ);
+ /* intentional fallthrough */
+ case INTERSECT_TEST:
+ break;
+ default:
+ throw new IllegalArgumentException("Improper level");
+ }
+ double[] weights = morph.getWeights();
+ for (int i = 0; i < weights.length; i++) {
+ GeometryArray ga = morph.getGeometryArray(i);
+ setCapabilities(ga, level);
+ }
+ } else if (node instanceof Shape3D) {
+ Shape3D shape = (Shape3D) node;
+ switch (level) {
+ case INTERSECT_FULL:
+ /* intentional fallthrough */
+ case INTERSECT_COORD:
+ shape.setCapability(Shape3D.ALLOW_GEOMETRY_READ);
+ /* intentional fallthrough */
+ case INTERSECT_TEST:
+ break;
+ default:
+ throw new IllegalArgumentException("Improper level");
+ }
+ for (int i = 0; i < shape.numGeometries(); i++) {
+ Geometry geo = shape.getGeometry(i);
+ if (geo instanceof GeometryArray) {
+ setCapabilities((GeometryArray)geo, level);
+ } else if (geo instanceof CompressedGeometry) {
+ setCapabilities((CompressedGeometry)geo, level);
+ }
+ }
+ } else {
+ throw new IllegalArgumentException("Improper node type");
+ }
+ }
+
+ static private void setCapabilities(GeometryArray ga, int level) {
+ switch (level) {
+ case INTERSECT_FULL:
+ ga.setCapability(GeometryArray.ALLOW_COLOR_READ);
+ ga.setCapability(GeometryArray.ALLOW_NORMAL_READ);
+ ga.setCapability(GeometryArray.ALLOW_TEXCOORD_READ);
+ /* intential fallthrough */
+ case INTERSECT_COORD:
+ ga.setCapability(GeometryArray.ALLOW_COUNT_READ);
+ ga.setCapability(GeometryArray.ALLOW_FORMAT_READ);
+ ga.setCapability(GeometryArray.ALLOW_COORDINATE_READ);
+ /* intential fallthrough */
+ case INTERSECT_TEST:
+ ga.setCapability(GeometryArray.ALLOW_INTERSECT);
+ break;
+ }
+ if (ga instanceof IndexedGeometryArray) {
+ setCapabilities((IndexedGeometryArray)ga, level);
+ }
+ }
+
+ static private void setCapabilities(IndexedGeometryArray iga, int level) {
+ switch (level) {
+ case INTERSECT_FULL:
+ iga.setCapability(IndexedGeometryArray.ALLOW_COLOR_INDEX_READ);
+ iga.setCapability(IndexedGeometryArray.ALLOW_NORMAL_INDEX_READ);
+ iga.setCapability(IndexedGeometryArray.ALLOW_TEXCOORD_INDEX_READ);
+ /* intential fallthrough */
+ case INTERSECT_COORD:
+ iga.setCapability(IndexedGeometryArray.ALLOW_COORDINATE_INDEX_READ);
+ /* intential fallthrough */
+ case INTERSECT_TEST:
+ break;
+ }
+ }
+
+ static private void setCapabilities(CompressedGeometry cg, int level) {
+ switch (level) {
+ case INTERSECT_FULL:
+ /* intential fallthrough */
+ case INTERSECT_COORD:
+ cg.setCapability(CompressedGeometry.ALLOW_GEOMETRY_READ);
+ /* intential fallthrough */
+ case INTERSECT_TEST:
+ cg.setCapability(CompressedGeometry.ALLOW_INTERSECT);
+ break;
+ }
+ }
+
+ // Methods used to define the pick shape
+
+ /** Sets the pick shape to a user-provided PickShape object
+ * @param ps The pick shape to pick against.
+ * @param startPt The start point to use for distance calculations
+ */
+ public void setShape (PickShape ps, Point3d startPt) {
+ this.pickShape = ps;
+ this.start = startPt;
+ userDefineShape = (ps != null);
+ }
+
+ /** Sets the pick shape to use a user-provided Bounds object
+ * @param bounds The bounds to pick against.
+ * @param startPt The start point to use for distance calculations
+ */
+ public void setShapeBounds (Bounds bounds, Point3d startPt) {
+ this.pickShape = (PickShape) new PickBounds (bounds);
+ this.start = startPt;
+ userDefineShape = true;
+ }
+
+ /** Sets the picking detail mode. The default is BOUNDS.
+ * @param mode One of BOUNDS, GEOMETRY, GEOMETRY_INTERSECT_INFO, or
+ * @exception IllegalArgumentException if mode is not a legal value
+ */
+ public void setMode (int mode) {
+ if ((mode != BOUNDS) && (mode != GEOMETRY) &&
+ (mode != GEOMETRY_INTERSECT_INFO)) {
+ throw new java.lang.IllegalArgumentException();
+ }
+ this.mode = mode;
+ }
+
+ /** Gets the picking detail mode.
+ */
+ public int getMode () {
+ return mode;
+ }
+
+ /** Sets the pick shape to a PickRay.
+ * @param start The start of the ray
+ * @param dir The direction of the ray
+ */
+ public void setShapeRay (Point3d start, Vector3d dir) {
+ this.pickShape = (PickShape) new PickRay (start, dir);
+ this.start = start;
+ userDefineShape = true;
+ }
+
+ /** Sets the pick shape to a PickSegment.
+ @param start The start of the segment
+p @param end The end of the segment
+ */
+ public void setShapeSegment (Point3d start, Point3d end) {
+ this.pickShape = (PickShape) new PickSegment (start, end);
+ this.start = start;
+ userDefineShape = true;
+ }
+
+ /** Sets the pick shape to a capped PickCylinder
+ * @param start The start of axis of the cylinder
+ * @param end The end of the axis of the cylinder
+ * @param radius The radius of the cylinder
+ */
+ public void setShapeCylinderSegment (Point3d start, Point3d end,
+ double radius) {
+ this.pickShape = (PickShape)
+ new PickCylinderSegment (start, end, radius);
+ this.start = start;
+ userDefineShape = true;
+ }
+
+ /** Sets the pick shape to an infinite PickCylinder.
+ * @param start The start of axis of the cylinder
+ * @param dir The direction of the axis of the cylinder
+ * @param radius The radius of the cylinder
+ */
+ public void setShapeCylinderRay (Point3d start, Vector3d dir,
+ double radius) {
+ this.pickShape = (PickShape) new PickCylinderRay (start, dir, radius);
+ this.start = start;
+ userDefineShape = true;
+ }
+
+ /** Sets the pick shape to a capped PickCone
+ * @param start The start of axis of the cone
+ * @param end The end of the axis of the cone
+ * @param angle The angle of the cone
+ */
+ public void setShapeConeSegment (Point3d start, Point3d end,
+ double angle) {
+ this.pickShape = (PickShape) new PickConeSegment (start, end, angle);
+ this.start = start;
+ userDefineShape = true;
+ }
+
+ /** Sets the pick shape to an infinite PickCone.
+ * @param start The start of axis of the cone
+ * @param dir The direction of the axis of the cone
+ * @param angle The angle of the cone
+ */
+ public void setShapeConeRay (Point3d start, Vector3d dir,
+ double angle) {
+ this.pickShape = (PickShape) new PickConeRay (start, dir, angle);
+ this.start = start;
+ userDefineShape = true;
+ }
+
+ /** Returns the PickShape for this object. */
+ public PickShape getPickShape () {
+ return pickShape;
+ }
+
+ /** Returns the start postion used for distance measurement. */
+ public Point3d getStartPosition () {
+ return start;
+ }
+
+ /** Selects all the nodes that intersect the PickShape.
+ @return An array of <code>PickResult</code> objects which will contain
+ information about the picked instances. <code>null</code> if nothing was
+ picked.
+ */
+ public PickResult[] pickAll () {
+ PickResult[] retval = null;
+ switch (mode) {
+ case BOUNDS:
+ retval = pickAll(pickShape);
+ break;
+ case GEOMETRY:
+ retval = pickGeomAll(pickShape);
+ break;
+ case GEOMETRY_INTERSECT_INFO:
+ retval = pickGeomAllIntersect(pickShape);
+ break;
+ default:
+ throw new java.lang.InternalError("Invalid pick mode");
+ }
+ return retval;
+ }
+
+ /** Select one of the nodes that intersect the PickShape
+ @return An array of <code>PickResult</code> objects which will contain
+ information about the picked instances. <code>null</code> if nothing
+ was picked.
+ */
+ public PickResult pickAny () {
+ PickResult retval = null;
+ switch (mode) {
+ case BOUNDS:
+ retval = pickAny(pickShape);
+ break;
+ case GEOMETRY:
+ retval = pickGeomAny(pickShape);
+ break;
+ case GEOMETRY_INTERSECT_INFO:
+ retval = pickGeomAnyIntersect(pickShape);
+ break;
+ default:
+ throw new java.lang.InternalError("Invalid pick mode");
+ }
+ return retval;
+ }
+
+ /** Select all the nodes that intersect the
+ PickShape, returned sorted. The "closest" object will be returned first.
+ See note above to see how "closest" is determined.
+ <p>
+ @return An array of <code>PickResult</code> objects which will contain
+ information
+ about the picked instances. <code>null</code> if nothing was picked.
+ */
+ public PickResult[] pickAllSorted () {
+ PickResult[] retval = null;
+
+ // System.out.println ("PickTool.pickAllSorted.");
+
+ switch (mode) {
+ case BOUNDS:
+ // System.out.println ("PickTool.pickAllSorted : Bounds");
+ retval = pickAllSorted(pickShape);
+ break;
+ case GEOMETRY:
+ // System.out.println ("PickTool.pickAllSorted : Geometry");
+ // TODO - BugId 4351050.
+ // pickGeomAllSorted is broken for PickCone and PickCylinder :
+ // The current Shape3D.intersect() API doesn't return the distance for
+ // PickCone and PickCylinder.
+ // 2) TODO - BugId 4351579.
+ // pickGeomClosest is broken for multi-geometry Shape3D node :
+ // The current Shape3D.intersect() API does't return the closest intersected
+ // geometry.
+ retval = pickGeomAllSorted(pickShape);
+
+ break;
+ case GEOMETRY_INTERSECT_INFO:
+ // System.out.println ("PickShape " + pickShape);
+ // System.out.println ("PickTool.pickAllSorted : GEOMETRY_INTERSECT_INFO");
+ retval = pickGeomAllSortedIntersect(pickShape);
+ break;
+ default:
+ throw new java.lang.InternalError("Invalid pick mode");
+ }
+ return retval;
+ }
+
+ /** Select the closest node that
+ intersects the PickShape. See note above to see how "closest" is
+ determined.
+ <p>
+ @return An array of <code>PickResult</code> objects which will contain
+ information about the picked instances. <code>null</code> if nothing
+ was picked.
+ */
+ public PickResult pickClosest () {
+ PickResult retval = null;
+ switch (mode) {
+ case BOUNDS:
+ retval = pickClosest(pickShape);
+ break;
+ case GEOMETRY:
+ // System.out.println("pickCloset -- Geometry based picking");
+ // 1) TODO - BugId 4351050.
+ // pickGeomClosest is broken for PickCone and PickCylinder :
+ // The current Shape3D.intersect() API doesn't return the distance for
+ // PickCone and PickCylinder.
+ // 2) TODO - BugId 4351579.
+ // pickGeomClosest is broken for multi-geometry Shape3D node :
+ // The current Shape3D.intersect() API does't return the closest intersected
+ // geometry.
+ retval = pickGeomClosest(pickShape);
+
+ break;
+ case GEOMETRY_INTERSECT_INFO:
+ // System.out.println ("PickShape " + pickShape);
+ // System.out.println ("PickTool.pickClosest : GEOMETRY_INTERSECT_INFO");
+ retval = pickGeomClosestIntersect(pickShape);
+ break;
+ default:
+ throw new java.lang.InternalError("Invalid pick mode");
+ }
+ return retval;
+ }
+
+ private PickResult[] pickAll (PickShape pickShape) {
+ PickResult[] pr = null;
+ SceneGraphPath[] sgp = null;
+
+ if (pickRootBG != null) {
+ sgp = pickRootBG.pickAll (pickShape);
+ } else if (pickRootL != null) {
+ sgp = pickRootL.pickAll (pickShape);
+ }
+ if (sgp == null) return null; // no match
+
+ // Create PickResult array
+ pr = new PickResult [sgp.length];
+ for (int i=0;i<sgp.length;i++) {
+ pr[i] = new PickResult (sgp[i], pickShape);
+ }
+ return pr;
+ }
+
+ private PickResult[] pickAllSorted (PickShape pickShape) {
+ PickResult[] pr = null;
+ SceneGraphPath[] sgp = null;
+
+ if (pickRootBG != null) {
+ sgp = pickRootBG.pickAllSorted (pickShape);
+ } else if (pickRootL != null) {
+ sgp = pickRootL.pickAllSorted (pickShape);
+ }
+ if (sgp == null) return null; // no match
+
+ // Create PickResult array
+ pr = new PickResult [sgp.length];
+ for (int i=0;i<sgp.length;i++) {
+ pr[i] = new PickResult (sgp[i], pickShape);
+ }
+ return pr;
+ }
+
+ private PickResult pickAny (PickShape pickShape) {
+ PickResult pr = null;
+ SceneGraphPath sgp = null;
+
+ if (pickRootBG != null) {
+ sgp = pickRootBG.pickAny (pickShape);
+ } else if (pickRootL != null) {
+ sgp = pickRootL.pickAny (pickShape);
+ }
+ if (sgp == null) return null; // no match
+
+ // Create PickResult object
+ pr = new PickResult (sgp, pickShape);
+ return pr;
+ }
+
+ private PickResult pickClosest (PickShape pickShape) {
+ PickResult pr = null;
+ SceneGraphPath sgp = null;
+
+ if (pickRootBG != null) {
+ sgp = pickRootBG.pickClosest (pickShape);
+ } else if (pickRootL != null) {
+ sgp = pickRootL.pickClosest (pickShape);
+ }
+ if (sgp == null) return null; // no match
+
+ // Create PickResult object
+ pr = new PickResult (sgp, pickShape);
+ return pr;
+ }
+
+ // ================================================================
+ // GEOMETRY METHODS
+ // ================================================================
+
+ private PickResult[] pickGeomAll (PickShape pickShape) {
+ SceneGraphPath[] sgp = null;
+ Node obj[] = null;
+ int i, cnt=0;
+
+ // First pass
+ if (pickRootBG != null) {
+ sgp = pickRootBG.pickAll(pickShape);
+ } else if (pickRootL != null) {
+ sgp = pickRootL.pickAll(pickShape);
+ }
+ if (sgp == null) return null; // no match
+
+ // Second pass, check to see if geometries intersected
+ boolean found[] = new boolean [sgp.length];
+
+ obj = new Node [sgp.length];
+ PickResult[] pr = new PickResult[sgp.length];
+ for (i=0; i<sgp.length; i++) {
+ obj[i] = sgp[i].getObject();
+ pr[i] = new PickResult (sgp[i], pickShape);
+
+ if (obj[i] instanceof Shape3D) {
+ found[i] = ((Shape3D) obj[i]).intersect(sgp[i], pickShape);
+ } else if (obj[i] instanceof Morph) {
+ found[i] = ((Morph) obj[i]).intersect(sgp[i], pickShape);
+ }
+ if (found[i] == true) cnt++;
+ }
+
+ if (cnt == 0) return null; // no match
+
+ PickResult[] newpr = new PickResult[cnt];
+ cnt = 0; // reset for reuse.
+ for(i=0; i<sgp.length; i++) {
+ if (found[i] == true)
+ pr[cnt++] = pr[i];
+ }
+
+ return pr;
+ }
+
+ private PickResult[] pickGeomAllSorted(PickShape pickShape) {
+ SceneGraphPath[] sgp = null;
+ Node[] obj = null;
+ int i, cnt=0;
+ double[] dist = new double[1];
+
+ // First pass
+ if (pickRootBG != null) {
+ sgp = pickRootBG.pickAll(pickShape);
+ } else if (pickRootL != null) {
+ sgp = pickRootL.pickAll(pickShape);
+ }
+ if (sgp == null) return null; // no match
+
+ /*
+ System.out.println ("PickTool.pickGeomAllSorted: bounds " +
+ "picking found "+sgp.length+" nodes");
+ */
+ // Second pass, check to see if geometries intersected
+ boolean[] found = new boolean [sgp.length];
+ double[] distArr = new double[sgp.length];
+ obj = new Node [sgp.length];
+ PickResult[] pr = new PickResult [sgp.length];
+
+ for (i=0; i<sgp.length; i++) {
+ obj[i] = sgp[i].getObject();
+ pr[i] = new PickResult (sgp[i], pickShape);
+ if (obj[i] instanceof Shape3D) {
+ found[i] = ((Shape3D)obj[i]).intersect(sgp[i], pickShape,
+ dist);
+ distArr[i] = dist[0];
+ } else if (obj[i] instanceof Morph) {
+ found[i] = ((Morph)obj[i]).intersect(sgp[i], pickShape,
+ dist);
+ distArr[i] = dist[0];
+ }
+ if (found[i] == true) cnt++;
+ }
+ if (cnt == 0) return null; // no match
+
+ PickResult[] npr = new PickResult [cnt];
+ double[] distance = new double [cnt];
+ cnt = 0; // reset for reuse.
+ for(i=0; i<sgp.length; i++) {
+ if (found[i] == true) {
+ distance[cnt] = distArr[i];
+ npr[cnt++] = pr[i];
+ }
+ }
+ if (cnt > 1) {
+ return sortPickResults (npr, distance);
+ } else { // Don't have to sort if only one item
+ return npr;
+ }
+ }
+
+ private PickResult pickGeomAny (PickShape pickShape) {
+ Node obj = null;
+ int i;
+ SceneGraphPath[] sgpa = null;
+
+ if (pickRootBG != null) {
+ sgpa = pickRootBG.pickAll(pickShape);
+ } else if (pickRootL != null) {
+ sgpa = pickRootL.pickAll(pickShape);
+ }
+
+ if (sgpa == null) return null; // no match
+
+ for(i=0; i<sgpa.length; i++) {
+ obj = sgpa[i].getObject();
+ PickResult pr = new PickResult(sgpa[i], pickShape);
+ if(obj instanceof Shape3D) {
+ if(((Shape3D) obj).intersect(sgpa[i], pickShape)) {
+ return pr;
+ }
+ } else if (obj instanceof Morph) {
+ if(((Morph) obj).intersect(sgpa[i], pickShape)){
+ return pr;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private PickResult pickGeomClosest(PickShape pickShape) {
+ // System.out.println("pickGeomCloset -- Geometry based picking");
+ PickResult[] pr = pickGeomAllSorted(pickShape);
+ if (pr == null) {
+ return null;
+ } else {
+ return pr[0];
+ }
+ }
+
+ // ================================================================
+ // NEW METHODS, return additional information
+ // ================================================================
+
+ private PickResult[] pickGeomAllIntersect(PickShape pickShape) {
+ SceneGraphPath[] sgp = null;
+ Node obj[] = null;
+ int i, cnt=0;
+
+ // First pass
+ if (pickRootBG != null) {
+ sgp = pickRootBG.pickAll(pickShape);
+ } else if (pickRootL != null) {
+ sgp = pickRootL.pickAll(pickShape);
+ }
+ if (sgp == null) return null; // no match
+
+ // Second pass, check to see if geometries intersected
+ boolean found[] = new boolean [sgp.length];
+
+ PickResult[] pr = new PickResult[sgp.length];
+ for (i=0; i<sgp.length; i++) {
+ pr[i] = new PickResult (sgp[i], pickShape);
+ if (pr[i].numIntersections() > 0) {
+ found[i] = true;
+ cnt++;
+ }
+ }
+
+ if (cnt == 0) return null; // no match
+
+ PickResult[] newpr = new PickResult[cnt];
+ cnt = 0; // reset for reuse.
+ for(i=0; i<sgp.length; i++) {
+ if(found[i] == true)
+ pr[cnt++] = pr[i];
+ }
+
+ return pr;
+ }
+
+ private PickResult[] pickGeomAllSortedIntersect (PickShape pickShape) {
+ SceneGraphPath[] sgp = null;
+ Node[] obj = null;
+ int i, cnt=0;
+ double[] dist = new double[1];
+
+ // First pass
+ if (pickRootBG != null) {
+ sgp = pickRootBG.pickAll(pickShape);
+ } else if (pickRootL != null) {
+ sgp = pickRootL.pickAll(pickShape);
+ }
+ if (sgp == null) return null; // no match
+
+
+ // System.out.println ("PickTool.pickGeomAllSortedIntersect: bounds " +
+ // " picking found "+sgp.length+" nodes");
+
+
+ // Second pass, check to see if geometries intersected
+ boolean[] found = new boolean[sgp.length];
+ double[] distArr = new double[sgp.length];
+
+ PickResult[] pr = new PickResult[sgp.length];
+ for (i=0; i<sgp.length; i++) {
+// pr[i] = new PickResult(sgp[i], pickShape);
+ pr[i] = getPickResult(sgp[i], pickShape);
+ int numIntersection = pr[i].numIntersections();
+ if (numIntersection > 0) {
+ // System.out.println ("numIntersection " + numIntersection);
+ found[i] = true;
+ double minDist;
+ double tempDist;
+
+ int minIndex;
+ boolean needToSwap = false;
+ minDist = pr[i].getIntersection(0).getDistance();
+ minIndex = 0;
+ for(int j=1; j<numIntersection; j++) {
+ // System.out.println ("Distance " + pr[i].getIntersection(j).getDistance());
+ //System.out.println ("Geom Index " + pr[i].getIntersection(j).getGeometryArrayIndex());
+ tempDist = pr[i].getIntersection(j).getDistance();
+ if(minDist > tempDist) {
+ minDist = tempDist;
+ minIndex = j;
+ needToSwap = true;
+ }
+ }
+
+ //Swap if necc.
+ if(needToSwap) {
+ // System.out.println ("Swap is needed");
+ PickIntersection pi0 = pr[i].getIntersection(0);
+ PickIntersection piMin = pr[i].getIntersection(minIndex);
+ pr[i].intersections.set(0, piMin);
+ pr[i].intersections.set(minIndex, pi0);
+ }
+
+ distArr[i] = pr[i].getIntersection(0).getDistance();
+ cnt++;
+ }
+ }
+
+
+ // System.out.println ("PickTool.pickGeomAllSortedIntersect: geometry intersect check "
+ // + " cnt " + cnt);
+
+
+ if (cnt == 0) return null; // no match
+
+ PickResult[] npr = new PickResult[cnt];
+ double[] distance = new double[cnt];
+ cnt = 0; // reset for reuse.
+ for(i=0; i<sgp.length; i++) {
+ if(found[i] == true) {
+ distance[cnt] = distArr[i];
+ npr[cnt++] = pr[i];
+ }
+ }
+
+ if (cnt > 1) {
+ return sortPickResults (npr, distance);
+ } else { // Don't have to sort if only one item
+ return npr;
+ }
+ }
+
+ private PickResult pickGeomClosestIntersect(PickShape pickShape) {
+ PickResult[] pr = pickGeomAllSortedIntersect(pickShape);
+ /*
+ System.out.println ("PickTool.pickGeomClosestIntersect: pr.length "
+ + pr.length);
+ for(int i=0;i<pr.length;i++) {
+ System.out.println ("pr["+i+"] " + pr[i]);
+ }
+ */
+
+ if (pr == null) {
+ return null;
+ } else {
+ for (int i = 1; i < pr.length; i++) {
+ freePickResult(pr[i]);
+ }
+ return pr[0];
+ }
+ }
+ private PickResult pickGeomAnyIntersect(PickShape pickShape) {
+ Node obj = null;
+ int i;
+ SceneGraphPath[] sgpa = null;
+
+ if (pickRootBG != null) {
+ sgpa = pickRootBG.pickAll(pickShape);
+ } else if (pickRootL != null) {
+ sgpa = pickRootL.pickAll(pickShape);
+ }
+ if (sgpa == null) return null; // no match
+ for(i=0; i<sgpa.length; i++) {
+ PickResult pr = new PickResult(sgpa[i], pickShape);
+ pr.setFirstIntersectOnly(true);
+ if (pr.numIntersections() > 0) {
+ return pr;
+ }
+ }
+
+ return null;
+ }
+
+ // ================================================================
+ // Sort Methods
+ // ================================================================
+ private PickResult[] sortPickResults (PickResult[] pr, double[] dist) {
+ int[] pos = new int [pr.length];
+ PickResult[] prsorted = new PickResult [pr.length];
+
+ // Initialize position array
+ for (int i=0; i<pr.length; i++) {
+ pos[i]=i;
+ }
+ // Do sort
+ quicksort (0, dist.length-1, dist, pos);
+
+ // Create new array
+ for (int i=0; i<pr.length; i++) {
+ prsorted[i] = pr[pos[i]];
+ }
+ return prsorted;
+ }
+
+ private final void quicksort( int l, int r, double[] dist,
+ int[] pos) {
+ int p,i,j;
+ double tmp,k;
+
+ i = l;
+ j = r;
+ k = dist[(l+r) / 2];
+ do {
+ while (dist[i]<k) i++;
+ while (k<dist[j]) j--;
+ if (i<=j) {
+ tmp = dist[i];
+ dist[i] =dist[j];
+ dist[j] = tmp;
+
+ p=pos[i];
+ pos[i]=pos[j];
+ pos[j]=p;
+ i++;
+ j--;
+ }
+ } while (i<=j);
+
+ if (l<j) quicksort(l, j, dist, pos);
+ if (l<r) quicksort(i, r, dist, pos);
+ }
+
+ PickResult getPickResult(SceneGraphPath spg, PickShape ps) {
+ PickResult pr = (PickResult)UtilFreelistManager.pickResultFreelist.getObject();
+ if (pr == null) {
+ pr = new PickResult();
+ }
+ pr.reset(spg, ps);
+ return pr;
+ }
+
+ void freePickResult(PickResult pr) {
+ UtilFreelistManager.pickResultFreelist.add(pr);
+ }
+
+} // PickTool
+
+
+
+
+
diff --git a/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickMouseBehavior.java b/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickMouseBehavior.java
new file mode 100644
index 0000000..64e9990
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickMouseBehavior.java
@@ -0,0 +1,182 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.picking.behaviors;
+
+import com.sun.j3d.utils.picking.*;
+import com.sun.j3d.internal.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+
+/**
+ * Base class that allows users to adding picking and mouse manipulation to
+ * the scene graph (see PickDragBehavior for an example of how to extend
+ * this base class). This class is useful for interactive apps.
+ */
+
+public abstract class PickMouseBehavior extends Behavior {
+
+ protected PickCanvas pickCanvas;
+
+ protected WakeupCriterion[] conditions;
+ protected WakeupOr wakeupCondition;
+ protected boolean buttonPress = false;
+
+ protected TransformGroup currGrp;
+ protected static final boolean debug = false;
+ protected MouseEvent mevent;
+
+ /**
+ * Creates a PickMouseBehavior given current canvas, root of the tree to
+ * operate on, and the bounds.
+ */
+ public PickMouseBehavior(Canvas3D canvas, BranchGroup root, Bounds bounds){
+ super();
+ currGrp = new TransformGroup();
+ currGrp.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+ currGrp.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
+ root.addChild(currGrp);
+ pickCanvas = new PickCanvas(canvas, root);
+ }
+
+ /**
+ * Sets the pick mode
+ * @see PickTool#setMode
+ **/
+ public void setMode(int pickMode) {
+ pickCanvas.setMode(pickMode);
+ }
+
+ /**
+ * Returns the pickMode
+ * @see PickTool#getMode
+ */
+
+ public int getMode() {
+ return pickCanvas.getMode();
+ }
+
+ /**
+ * Sets the pick tolerance
+ * @see PickCanvas#setTolerance
+ */
+ public void setTolerance(float tolerance) {
+ pickCanvas.setTolerance(tolerance);
+ }
+
+ /**
+ * Returns the pick tolerance
+ * @see PickCanvas#getTolerance
+ */
+ public float getTolerance() {
+ return pickCanvas.getTolerance();
+ }
+
+ public void initialize() {
+
+ conditions = new WakeupCriterion[2];
+ conditions[0] = new WakeupOnAWTEvent(Event.MOUSE_MOVE);
+ conditions[1] = new WakeupOnAWTEvent(Event.MOUSE_DOWN);
+ wakeupCondition = new WakeupOr(conditions);
+
+ wakeupOn(wakeupCondition);
+ }
+
+ private void processMouseEvent(MouseEvent evt) {
+ buttonPress = false;
+
+ if (evt.getID()==MouseEvent.MOUSE_PRESSED |
+ evt.getID()==MouseEvent.MOUSE_CLICKED) {
+ buttonPress = true;
+ return;
+ }
+ else if (evt.getID() == MouseEvent.MOUSE_MOVED) {
+ // Process mouse move event
+ }
+ }
+
+ public void processStimulus (Enumeration criteria) {
+ WakeupCriterion wakeup;
+ AWTEvent[] evt = null;
+ int xpos = 0, ypos = 0;
+
+ while(criteria.hasMoreElements()) {
+ wakeup = (WakeupCriterion)criteria.nextElement();
+ if (wakeup instanceof WakeupOnAWTEvent)
+ evt = ((WakeupOnAWTEvent)wakeup).getAWTEvent();
+ }
+
+ if (evt[0] instanceof MouseEvent){
+ mevent = (MouseEvent) evt[0];
+
+ if (debug)
+ System.out.println("got mouse event");
+ processMouseEvent((MouseEvent)evt[0]);
+ xpos = mevent.getPoint().x;
+ ypos = mevent.getPoint().y;
+ }
+
+ if (debug)
+ System.out.println("mouse position " + xpos + " " + ypos);
+
+ if (buttonPress){
+ updateScene(xpos, ypos);
+ }
+ wakeupOn (wakeupCondition);
+ }
+
+ /** Subclasses shall implement this update function
+ */
+ public abstract void updateScene(int xpos, int ypos);
+
+ void freePickResult(PickResult pr) {
+ UtilFreelistManager.pickResultFreelist.add(pr);
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickRotateBehavior.java b/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickRotateBehavior.java
new file mode 100644
index 0000000..a1a3ded
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickRotateBehavior.java
@@ -0,0 +1,172 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.picking.behaviors;
+
+import com.sun.j3d.utils.picking.*;
+import com.sun.j3d.utils.behaviors.mouse.*;
+import com.sun.j3d.internal.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+/**
+ * A mouse behavior that allows user to pick and drag scene graph objects.
+ * Common usage:
+ * <p>
+ * 1. Create your scene graph.
+ * <p>
+ * 2. Create this behavior with root and canvas.
+ * <p>
+ * <blockquote><pre>
+ * PickRotateBehavior behavior = new PickRotateBehavior(canvas, root, bounds);
+ * root.addChild(behavior);
+ * </pre></blockquote>
+ * <p>
+ * The above behavior will monitor for any picking events on
+ * the scene graph (below root node) and handle mouse drags on pick hits.
+ * Note the root node can also be a subgraph node of the scene graph (rather
+ * than the topmost).
+ */
+
+public class PickRotateBehavior extends PickMouseBehavior implements MouseBehaviorCallback {
+ MouseRotate drag;
+// int pickMode = PickTool.BOUNDS;
+ private PickingCallback callback=null;
+ private TransformGroup currentTG;
+
+ /**
+ * Creates a pick/rotate behavior that waits for user mouse events for
+ * the scene graph. This method has its pickMode set to BOUNDS picking.
+ * @param root Root of your scene graph.
+ * @param canvas Java 3D drawing canvas.
+ * @param bounds Bounds of your scene.
+ **/
+
+ public PickRotateBehavior(BranchGroup root, Canvas3D canvas, Bounds bounds){
+ super(canvas, root, bounds);
+ drag = new MouseRotate(MouseRotate.MANUAL_WAKEUP);
+ drag.setTransformGroup(currGrp);
+ currGrp.addChild(drag);
+ drag.setSchedulingBounds(bounds);
+ this.setSchedulingBounds(bounds);
+ }
+
+ /**
+ * Creates a pick/rotate behavior that waits for user mouse events for
+ * the scene graph.
+ * @param root Root of your scene graph.
+ * @param canvas Java 3D drawing canvas.
+ * @param bounds Bounds of your scene.
+ * @param pickMode specifys PickTool.BOUNDS, PickTool.GEOMETRY or
+ * PickTool.GEOMETRY_INTERSECT_INFO.
+ * @see PickTool#setMode
+ **/
+
+ public PickRotateBehavior(BranchGroup root, Canvas3D canvas, Bounds bounds,
+ int pickMode){
+ super(canvas, root, bounds);
+ drag = new MouseRotate(MouseRotate.MANUAL_WAKEUP);
+ drag.setTransformGroup(currGrp);
+ currGrp.addChild(drag);
+ drag.setSchedulingBounds(bounds);
+ this.setSchedulingBounds(bounds);
+ this.setMode(pickMode);
+ }
+
+
+ /**
+ * Update the scene to manipulate any nodes. This is not meant to be
+ * called by users. Behavior automatically calls this. You can call
+ * this only if you know what you are doing.
+ *
+ * @param xpos Current mouse X pos.
+ * @param ypos Current mouse Y pos.
+ **/
+ public void updateScene(int xpos, int ypos){
+ TransformGroup tg = null;
+
+ if (!mevent.isMetaDown() && !mevent.isAltDown()){
+
+ pickCanvas.setShapeLocation(xpos, ypos);
+ PickResult pr = pickCanvas.pickClosest();
+ if ((pr != null) &&
+ ((tg = (TransformGroup)pr.getNode(PickResult.TRANSFORM_GROUP))
+ != null) &&
+ (tg.getCapability(TransformGroup.ALLOW_TRANSFORM_READ)) &&
+ (tg.getCapability(TransformGroup.ALLOW_TRANSFORM_WRITE))){
+ drag.setTransformGroup(tg);
+ drag.wakeup();
+ currentTG = tg;
+ // free the PickResult
+ freePickResult(pr);
+ } else if (callback!=null)
+ callback.transformChanged( PickingCallback.NO_PICK, null );
+ }
+ }
+
+ /**
+ * Callback method from MouseRotate
+ * This is used when the Picking callback is enabled
+ */
+ public void transformChanged( int type, Transform3D transform ) {
+ callback.transformChanged( PickingCallback.ROTATE, currentTG );
+ }
+
+ /**
+ * Register the class @param callback to be called each
+ * time the picked object moves
+ */
+ public void setupCallback( PickingCallback callback ) {
+ this.callback = callback;
+ if (callback==null)
+ drag.setupCallback( null );
+ else
+ drag.setupCallback( this );
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickTranslateBehavior.java b/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickTranslateBehavior.java
new file mode 100644
index 0000000..8f7d716
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickTranslateBehavior.java
@@ -0,0 +1,157 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.picking.behaviors;
+
+import com.sun.j3d.utils.picking.*;
+import com.sun.j3d.utils.behaviors.mouse.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+/**
+ * A mouse behavior that allows user to pick and translate scene graph objects.
+ * Common usage: 1. Create your scene graph. 2. Create this behavior with
+ * the root and canvas. See PickRotateBehavior for more details.
+ */
+
+public class PickTranslateBehavior extends PickMouseBehavior implements MouseBehaviorCallback {
+ MouseTranslate translate;
+ private PickingCallback callback = null;
+ private TransformGroup currentTG;
+
+ /**
+ * Creates a pick/translate behavior that waits for user mouse events for
+ * the scene graph.
+ * @param root Root of your scene graph.
+ * @param canvas Java 3D drawing canvas.
+ * @param bounds Bounds of your scene.
+ **/
+
+ public PickTranslateBehavior(BranchGroup root, Canvas3D canvas, Bounds bounds){
+ super(canvas, root, bounds);
+ translate = new MouseTranslate(MouseBehavior.MANUAL_WAKEUP);
+ translate.setTransformGroup(currGrp);
+ currGrp.addChild(translate);
+ translate.setSchedulingBounds(bounds);
+ this.setSchedulingBounds(bounds);
+ }
+
+ /**
+ * Creates a pick/translate behavior that waits for user mouse events for
+ * the scene graph.
+ * @param root Root of your scene graph.
+ * @param canvas Java 3D drawing canvas.
+ * @param bounds Bounds of your scene.
+ * @param pickMode specifys PickTool.BOUNDS, PickTool.GEOMETRY or
+ * PickTool.GEOMETRY_INTERSECT_INFO.
+ * @see PickTool#setMode
+ **/
+ public PickTranslateBehavior(BranchGroup root, Canvas3D canvas,
+ Bounds bounds, int pickMode) {
+ super(canvas, root, bounds);
+ translate = new MouseTranslate(MouseBehavior.MANUAL_WAKEUP);
+ translate.setTransformGroup(currGrp);
+ currGrp.addChild(translate);
+ translate.setSchedulingBounds(bounds);
+ this.setSchedulingBounds(bounds);
+ this.setMode(pickMode);
+ }
+
+
+ /**
+ * Update the scene to manipulate any nodes. This is not meant to be
+ * called by users. Behavior automatically calls this. You can call
+ * this only if you know what you are doing.
+ *
+ * @param xpos Current mouse X pos.
+ * @param ypos Current mouse Y pos.
+ **/
+ public void updateScene(int xpos, int ypos){
+ TransformGroup tg = null;
+
+ if (!mevent.isAltDown() && mevent.isMetaDown()){
+
+ pickCanvas.setShapeLocation(xpos, ypos);
+ PickResult pr = pickCanvas.pickClosest();
+ if ((pr != null) &&
+ ((tg = (TransformGroup)pr.getNode(PickResult.TRANSFORM_GROUP))
+ != null) &&
+ (tg.getCapability(TransformGroup.ALLOW_TRANSFORM_READ)) &&
+ (tg.getCapability(TransformGroup.ALLOW_TRANSFORM_WRITE))){
+
+ translate.setTransformGroup(tg);
+ translate.wakeup();
+ currentTG = tg;
+ freePickResult(pr);
+ } else if (callback!=null)
+ callback.transformChanged( PickingCallback.NO_PICK, null );
+ }
+
+ }
+
+ /**
+ * Callback method from MouseTranslate
+ * This is used when the Picking callback is enabled
+ */
+ public void transformChanged( int type, Transform3D transform ) {
+ callback.transformChanged( PickingCallback.TRANSLATE, currentTG );
+ }
+
+ /**
+ * Register the class @param callback to be called each
+ * time the picked object moves
+ */
+ public void setupCallback( PickingCallback callback ) {
+ this.callback = callback;
+ if (callback==null)
+ translate.setupCallback( null );
+ else
+ translate.setupCallback( this );
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickZoomBehavior.java b/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickZoomBehavior.java
new file mode 100644
index 0000000..1db2b31
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickZoomBehavior.java
@@ -0,0 +1,155 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.picking.behaviors;
+
+import com.sun.j3d.utils.picking.*;
+import com.sun.j3d.utils.behaviors.mouse.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+
+/**
+ * A mouse behavior that allows user to pick and zoom scene graph objects.
+ * Common usage: 1. Create your scene graph. 2. Create this behavior with
+ * the root and canvas. See PickRotateBehavior for more details.
+ */
+
+public class PickZoomBehavior extends PickMouseBehavior implements MouseBehaviorCallback {
+ MouseZoom zoom;
+ private PickingCallback callback = null;
+ private TransformGroup currentTG;
+
+ /**
+ * Creates a pick/zoom behavior that waits for user mouse events for
+ * the scene graph.
+ * @param root Root of your scene graph.
+ * @param canvas Java 3D drawing canvas.
+ * @param bounds Bounds of your scene.
+ **/
+
+ public PickZoomBehavior(BranchGroup root, Canvas3D canvas, Bounds bounds){
+ super(canvas, root, bounds);
+ zoom = new MouseZoom(MouseBehavior.MANUAL_WAKEUP);
+ zoom.setTransformGroup(currGrp);
+ currGrp.addChild(zoom);
+ zoom.setSchedulingBounds(bounds);
+ this.setSchedulingBounds(bounds);
+ }
+
+ /**
+ * Creates a pick/zoom behavior that waits for user mouse events for
+ * the scene graph.
+ * @param root Root of your scene graph.
+ * @param canvas Java 3D drawing canvas.
+ * @param bounds Bounds of your scene.
+ * @param pickMode specifys PickTool.BOUNDS, PickTool.GEOMETRY or
+ * PickTool.GEOMETRY_INTERSECT_INFO.
+ * @see PickTool#setMode
+ */
+ public PickZoomBehavior(BranchGroup root, Canvas3D canvas, Bounds bounds,
+ int pickMode) {
+ super(canvas, root, bounds);
+ zoom = new MouseZoom(MouseBehavior.MANUAL_WAKEUP);
+ zoom.setTransformGroup(currGrp);
+ currGrp.addChild(zoom);
+ zoom.setSchedulingBounds(bounds);
+ this.setSchedulingBounds(bounds);
+ this.setMode(pickMode);
+ }
+
+ /**
+ * Update the scene to manipulate any nodes. This is not meant to be
+ * called by users. Behavior automatically calls this. You can call
+ * this only if you know what you are doing.
+ *
+ * @param xpos Current mouse X pos.
+ * @param ypos Current mouse Y pos.
+ **/
+
+ public void updateScene(int xpos, int ypos){
+ TransformGroup tg = null;
+
+ if (mevent.isAltDown() && !mevent.isMetaDown()){
+
+ pickCanvas.setShapeLocation(xpos, ypos);
+ PickResult pr = pickCanvas.pickClosest();
+ if ((pr != null) &&
+ ((tg = (TransformGroup)pr.getNode(PickResult.TRANSFORM_GROUP))
+ != null) &&
+ (tg.getCapability(TransformGroup.ALLOW_TRANSFORM_READ)) &&
+ (tg.getCapability(TransformGroup.ALLOW_TRANSFORM_WRITE))){
+ zoom.setTransformGroup(tg);
+ zoom.wakeup();
+ currentTG = tg;
+ freePickResult(pr);
+ } else if (callback!=null)
+ callback.transformChanged( PickingCallback.NO_PICK, null );
+ }
+ }
+
+ /**
+ * Callback method from MouseZoom
+ * This is used when the Picking callback is enabled
+ */
+ public void transformChanged( int type, Transform3D transform ) {
+ callback.transformChanged( PickingCallback.ZOOM, currentTG );
+ }
+
+ /**
+ * Register the class @param callback to be called each
+ * time the picked object moves
+ */
+ public void setupCallback( PickingCallback callback ) {
+ this.callback = callback;
+ if (callback==null)
+ zoom.setupCallback( null );
+ else
+ zoom.setupCallback( this );
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickingCallback.java b/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickingCallback.java
new file mode 100644
index 0000000..fdb268b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/picking/behaviors/PickingCallback.java
@@ -0,0 +1,76 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.picking.behaviors;
+
+import javax.media.j3d.TransformGroup;
+
+/**
+ * The PickingCallback interface allows a class to be notified when a
+ * picked object is moved. The class that is interested in object
+ * movement implements this interface, and the object created with
+ * that class is registered with the desired subclass of
+ * PickMouseBehavior using the <code>setupCallback</code> method.
+ * When the picked object moves, the registered object's
+ * <code>transformChanged</code> method is invoked.
+ */
+
+public interface PickingCallback {
+
+ public final static int ROTATE=0;
+ public final static int TRANSLATE=1;
+ public final static int ZOOM=2;
+
+ /**
+ * The user made a selection but nothing was
+ * actually picked
+ */
+ public final static int NO_PICK=3;
+
+ /**
+ * Called by the Pick Behavior with which this callback
+ * is registered each time the Picked object is moved
+ */
+ public void transformChanged( int type, TransformGroup tg );
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/NamedObjectException.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/NamedObjectException.java
new file mode 100644
index 0000000..73ed757
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/NamedObjectException.java
@@ -0,0 +1,68 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io;
+
+/**
+ * An error has occurred while processing a named object
+ */
+public class NamedObjectException extends java.lang.Exception {
+
+ /**
+ * Creates new <code>NoSuchNameException</code> without detail message.
+ */
+ public NamedObjectException() {
+ }
+
+
+ /**
+ * Constructs an <code>NoSuchNameException</code> with the specified detail message.
+ * @param msg the detail message.
+ */
+ public NamedObjectException(String msg) {
+ super(msg);
+ }
+}
+
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/ObjectNotLoadedException.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/ObjectNotLoadedException.java
new file mode 100644
index 0000000..ffe83c4
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/ObjectNotLoadedException.java
@@ -0,0 +1,68 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io;
+
+/**
+ * The named object has not been loaded so it's instance can not be returned
+ */
+public class ObjectNotLoadedException extends java.lang.Exception {
+
+ /**
+ * Creates new <code>ObjectNotLoadedException</code> without detail message.
+ */
+ public ObjectNotLoadedException() {
+ }
+
+
+ /**
+ * Constructs an <code>ObjectNotLoadedException</code> with the specified detail message.
+ * @param msg the detail message.
+ */
+ public ObjectNotLoadedException(String msg) {
+ super(msg);
+ }
+}
+
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphFileReader.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphFileReader.java
new file mode 100644
index 0000000..5f9b182
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphFileReader.java
@@ -0,0 +1,224 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.media.j3d.VirtualUniverse;
+import javax.media.j3d.BranchGroup;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Canvas3D;
+import com.sun.j3d.utils.universe.ConfiguredUniverse;
+
+import com.sun.j3d.utils.scenegraph.io.retained.RandomAccessFileControl;
+
+/**
+ * Read Java3D BranchGraphs and/or Universe from a file. Individual branchgraphs or an
+ * entire Universe can be read and references (shared nodes and components)
+ * between the graphs are handled correctly.
+ */
+public class SceneGraphFileReader extends java.lang.Object {
+
+ private RandomAccessFileControl fileControl;
+
+ /**
+ * Creates new SceneGraphFileReader.
+ */
+ public SceneGraphFileReader( java.io.File file ) throws IOException {
+ fileControl = new RandomAccessFileControl();
+ fileControl.openFile( file );
+ }
+
+ /**
+ * Create and return a ConfiguredUniverse with the PlatformGeometry, ViewerAvatar,
+ * and Locales saved in the file. The MultiTransformGroup between the ViewingPlatform
+ * and the View is also restored. Universe configuration information is retrieved
+ * via <code>ConfiguredUniverse.getConfigURL()</code>.<p>
+ * If the file does not contain universe information, null is returned.<p>
+ *
+ * @param attachBranchGraphs load and attach all the branchgraphs
+ * to the universe.
+ * @see ConfiguredUniverse#getConfigURL
+ */
+ public ConfiguredUniverse readUniverse(boolean attachBranchGraphs ) throws IOException {
+ return fileControl.readUniverse( attachBranchGraphs, null );
+ }
+
+ /**
+ * Set the ClassLoader used to load the scene graph objects and
+ * deserialize user data
+ */
+ public void setClassLoader( ClassLoader classLoader ) {
+ fileControl.setClassLoader( classLoader );
+ }
+
+ /**
+ * Get the ClassLoader used to load the scene graph objects and
+ * deserialize user data
+ */
+ public ClassLoader getClassLoader() {
+ return fileControl.getClassLoader();
+ }
+
+ /**
+ * Create and return a ConfiguredUniverse with the PlatformGeometry, ViewerAvatar,
+ * and Locales saved in the file. The MultiTransformGroup between the ViewingPlatform
+ * and the View is also restored.<p>
+ * If the file does not contain universe information, null is returned.<p>
+ *
+ * @param attachBranchGraphs load and attach all the branchgraphs
+ * to the universe.
+ * @param canvas The canvas to be associated with the Universe.
+ */
+ public ConfiguredUniverse readUniverse(boolean attachBranchGraphs,
+ Canvas3D canvas) throws IOException {
+ return fileControl.readUniverse( attachBranchGraphs, canvas );
+ }
+
+ /**
+ * Get the UserData in the File header
+ */
+ public Object readUserData() throws IOException {
+ return fileControl.getUserData();
+ }
+
+ /**
+ * Get the Description of this file's contents
+ */
+ public String readDescription() throws IOException {
+ return fileControl.readFileDescription();
+ }
+
+ /**
+ * Return the number of BranchGraphs in the file
+ */
+ public int getBranchGraphCount() {
+ return fileControl.getBranchGraphCount();
+ }
+
+ /**
+ * Read the BranchGraph at index in the file. If the graph
+ * contains references to nodes in other BranchGraphs that have not already been
+ * loaded, they will also be loaded and returned.<p>
+ *
+ * The requested graph will always be the first element in the array.<p>
+ *
+ * The file index of all the Graphs can be discovered using <code>getBranchGraphPosition</code>.<p>
+ *
+ * @param index The index of the Graph in the file. First graph is at index 0
+ *
+ * @see #getBranchGraphPosition( BranchGroup graph )
+ *
+ */
+ public BranchGroup[] readBranchGraph(int index) throws IOException {
+ return fileControl.readBranchGraph( index );
+ }
+
+ /**
+ * Read and return all the branchgraphs in the file
+ */
+ public BranchGroup[] readAllBranchGraphs() throws IOException {
+ return fileControl.readAllBranchGraphs();
+ }
+
+ /**
+ * Remove the IO system's reference to this branchgraph and all its nodes.<p>
+ *
+ * References to all loaded graphs are maintained by the IO system in
+ * order to facilitate node and component sharing between the graphs.<p>
+ *
+ * This call removes the references to graph <code>index</code><p>
+ *
+ * NOT CURRENTLY IMPLEMENTED
+ */
+ public void dereferenceBranchGraph( BranchGroup graph ) {
+ throw new RuntimeException("Not implemented");
+ }
+
+ /**
+ * Given a BranchGraph that has been loaded return the index of the
+ * graph in the file. The the Branchgroup isn't found, -1 is returned.
+ */
+ public int getBranchGraphPosition( BranchGroup graph ) {
+ return fileControl.getBranchGraphPosition( graph );
+ }
+
+ /**
+ * Read the userdata for the branchgraph at 'index' in the file
+ *
+ * @param index the index of the graph in the file
+ */
+ public Object readBranchGraphUserData( int index ) throws IOException {
+ return fileControl.readBranchGraphUserData( index );
+ }
+
+ /**
+ * Return the names of all the named objects
+ */
+ public String[] getNames() {
+ return fileControl.getNames();
+ }
+
+ /**
+ * Return the named object.
+ *
+ * @param name The name of the object
+ *
+ * @exception NamedObjectException is thrown if the name is not known to the system
+ * @exception ObjectNotLoadedException is thrown if the named object has not been loaded yet
+ */
+ public SceneGraphObject getNamedObject( String name ) throws NamedObjectException, ObjectNotLoadedException {
+ return fileControl.getNamedObject( name );
+ }
+
+ /**
+ * Close the file and cleanup internal data structures
+ */
+ public void close() throws IOException {
+ fileControl.close();
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphFileWriter.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphFileWriter.java
new file mode 100644
index 0000000..9c6907e
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphFileWriter.java
@@ -0,0 +1,157 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Iterator;
+
+import javax.media.j3d.BranchGroup;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.CapabilityNotSetException;
+import com.sun.j3d.utils.scenegraph.io.retained.RandomAccessFileControl;
+import com.sun.j3d.utils.universe.SimpleUniverse;
+
+/**
+ * Write a (set) of Java3D BranchGraphs and/or Universe to a file. The BranchGraphs
+ * are stored in the order in which they are written, they can be read in any order
+ * using SceneGraphFileReader.
+ *
+ * The API handles Nodes and NodeComponents that are shared between seperate
+ * graphs. It will handle all Java3D 1.3 core classes and any user
+ * subclass of a Node or NodeComponent that implements the SceneGraphIO
+ * interface.
+ */
+public class SceneGraphFileWriter extends java.lang.Object {
+
+ private RandomAccessFileControl fileControl;
+ private File file;
+
+ /** Creates new SceneGraphFileWriter and opens the file for writing.
+ *
+ * <P>Writes the
+ * Java3D Universe structure to the file. This includes the number and position of
+ * the Locales, PlatformGeometry, ViewerAvatar, and the MultitransformGroup between
+ * the ViewingPlatform and the View. However this
+ * call does not write the content of the branch graphs unless writeUniverseContent is true.
+ * <code>universe</code> may be null.
+ * This call will overwrite any existing universe, fileDescription and
+ * userData in the file.</P>
+ *
+ * <P>close() MUST be called when IO is complete. If close() is not called
+ * the file contents will be undefined.</P>
+ *
+ * @param file The file to write the data to
+ * @param universe The SimpleUniverse to write
+ * @param writeUniverseContent If true, the content of the Locales will be written.
+ * Otherwise just the universe configuration data will be written.
+ * @param fileDescription A description of the file's content
+ * @param fileUserData User defined object
+ *
+ * @exception IOException Thrown if there are any IO errors
+ * @exception UnsupportedUniverseException Thrown if <code>universe</code> is not
+ * a supported universe class. Currently SimpleUniverse and ConfiguredUniverse
+ * are supported.
+ */
+ public SceneGraphFileWriter( java.io.File file,
+ SimpleUniverse universe,
+ boolean writeUniverseContent,
+ String fileDescription,
+ java.io.Serializable fileUserData) throws IOException, UnsupportedUniverseException {
+ fileControl = new RandomAccessFileControl();
+ this.file = file;
+ file.createNewFile();
+
+ if (!file.canWrite())
+ throw new IOException( "Can not Write to File" );
+
+ fileControl.createFile( file, universe, writeUniverseContent, fileDescription, fileUserData );
+ }
+
+ /**
+ * Write the graph to the end of the file.
+ *
+ * close() MUST be called when IO is complete. If close() is not called
+ * the file contents will be undefined.
+ */
+ public void writeBranchGraph( BranchGroup graph ) throws IOException {
+ writeBranchGraph( graph, null );
+ }
+
+ /**
+ * Write a branch graph and some user associated data to the
+ * end of the file.
+ *
+ * close() MUST be called when IO is complete. If close() is not called
+ * the file contents will be undefined.
+ */
+ public void writeBranchGraph( BranchGroup graph,
+ java.io.Serializable data ) throws IOException {
+ fileControl.writeBranchGraph( graph, data );
+ }
+
+ /**
+ * Add a named reference to a SceneGraphObject in the file.
+ *
+ * <code>object</code> must have been written to the file before this method is
+ * called. If the object is not in the file a NamedObjectException will be thrown.
+ *
+ * Adding duplicate names will result in the old name being overwritten.
+ * Different names can reference the same object
+ */
+ public void addObjectName( String name, SceneGraphObject object ) throws NamedObjectException {
+ fileControl.addNamedObject( name, object );
+ }
+
+ /**
+ * Close the file and cleanup internal data structures.
+ */
+ public void close() throws IOException {
+ fileControl.close();
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphIO.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphIO.java
new file mode 100644
index 0000000..598c37c
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphIO.java
@@ -0,0 +1,112 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io;
+
+/**
+ * Implement this interface in any classes that subclass a Java3D SceneGraphObject
+ * in order to have your class handled correctly by scenegraph.io.
+ *
+ * More information and example code is provided <A href="doc-files/extensibility.html">here.</a>
+ *
+ * Classes that implement this interface MUST have a no-arg constructor
+ */
+public interface SceneGraphIO {
+
+ /**
+ * The method is called before writeSGObject and gives the user the chance
+ * to create references to other Nodes and NodeComponents.
+ *
+ * References take the form of a nodeID, of type integer. Every SceneGraphObject
+ * is assigned a unique ID.
+ *
+ * The user must save the reference information in writeSGObject
+ *
+ * @param ref provides methods to create references to a SceneGraphObject
+ */
+ public void createSceneGraphObjectReferences( SceneGraphObjectReferenceControl ref );
+
+ /**
+ * Within this method the user should restore references to the SceneGraphObjects
+ * whose nodeID's were created with createSceneGraphObjectReferences
+ * This method is called once the all objects in the scenegraph have been loaded.
+ *
+ *
+ * @param ref provides methods to resolve references to a SceneGraphObject
+ */
+ public void restoreSceneGraphObjectReferences( SceneGraphObjectReferenceControl ref );
+
+ /**
+ * This method should store all the local state of the object and any references
+ * to other SceneGraphObjects into <code>out</code>.
+ *
+ * This is called after data for the parent SceneGraphObject has been written to
+ * the <code>out</code>.
+ *
+ * @param out the output stream
+ */
+ public void writeSceneGraphObject( java.io.DataOutput out ) throws java.io.IOException;
+
+ /**
+ * This is called after the object has been constructed and the superclass SceneGraphObject
+ * data has been read from <code>in</code>.
+ *
+ * The user should restore all state infomation written in writeSGObject
+ *
+ * @param in the input stream
+ */
+ public void readSceneGraphObject( java.io.DataInput in ) throws java.io.IOException;
+
+ /**
+ * Flag indicating for children of this object should be saved
+ *
+ * This method only has an effect if this is a subclass of Group.
+ *
+ * If this returns true then all children of this Group will be saved.
+ *
+ * If it returns false then the children are not saved.
+ */
+ public boolean saveChildren();
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphObjectReferenceControl.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphObjectReferenceControl.java
new file mode 100644
index 0000000..34708be
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphObjectReferenceControl.java
@@ -0,0 +1,69 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io;
+
+/**
+ * Provides and resolves references to SceneGraphObjects to enable
+ * persistant references in user defined SceneGraphObjects implementing
+ * the SceneGraphIO interface.
+ */
+public interface SceneGraphObjectReferenceControl {
+
+ /**
+ * Add a reference to the scenegraph object specified and return
+ * the nodeID for the object
+ *
+ * Use only during the save cycle
+ */
+ public int addReference( javax.media.j3d.SceneGraphObject object );
+
+ /**
+ * Given a nodeID return the corresponding scene graph object.
+ *
+ * Use only during the load cycle
+ */
+ public javax.media.j3d.SceneGraphObject resolveReference( int nodeID );
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphStreamReader.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphStreamReader.java
new file mode 100644
index 0000000..6b2c64a
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphStreamReader.java
@@ -0,0 +1,117 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io;
+
+import java.io.InputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+
+import javax.media.j3d.BranchGroup;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Canvas3D;
+import com.sun.j3d.utils.scenegraph.io.retained.StreamControl;
+import com.sun.j3d.utils.universe.ConfiguredUniverse;
+
+/**
+ * Read and create a (set) of Java3D BranchGraphs or Universe from a Java Stream.
+ */
+public class SceneGraphStreamReader extends java.lang.Object {
+
+ private StreamControl control;
+ private DataInputStream in;
+
+ /** Creates new SceneGraphStreamReader and reads the file header information */
+ public SceneGraphStreamReader( InputStream stream ) throws IOException {
+ in = new DataInputStream( stream );
+ control = new StreamControl( in );
+ control.readStreamHeader();
+ }
+
+ /**
+ * Read and create the universe. If the BranchGraphs were written then
+ * they will be added to the universe before it is returned.
+ */
+ public ConfiguredUniverse readUniverse() throws IOException {
+ return control.readUniverse(in, true, null);
+ }
+
+ /**
+ * Read and create the universe. If the BranchGraphs were written then
+ * they will be added to the universe before it is returned.
+ * @param canvas The Canvas3D to associate with the universe.
+ */
+ public ConfiguredUniverse readUniverse(Canvas3D canvas) throws IOException {
+ return control.readUniverse(in, true, canvas);
+ }
+
+ /**
+ * Read and return the graph from the stream.
+ * <code>namedObjects</code> map will be updated with any objects that
+ * were named during the write process
+ */
+ public BranchGroup readBranchGraph( HashMap namedObjects ) throws IOException {
+ return control.readBranchGraph( namedObjects );
+ }
+
+ /**
+ * Set the ClassLoader used to load the scene graph objects and
+ * deserialize user data
+ */
+ public void setClassLoader( ClassLoader classLoader ) {
+ control.setClassLoader( classLoader );
+ }
+
+ /**
+ * Get the ClassLoader used to load the scene graph objects and
+ * deserialize user data
+ */
+ public ClassLoader getClassLoader() {
+ return control.getClassLoader();
+ }
+
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphStreamWriter.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphStreamWriter.java
new file mode 100644
index 0000000..81eb89b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/SceneGraphStreamWriter.java
@@ -0,0 +1,127 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.DataOutputStream;
+import java.util.HashMap;
+
+import javax.media.j3d.BranchGroup;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.CapabilityNotSetException;
+import javax.media.j3d.DanglingReferenceException;
+import com.sun.j3d.utils.scenegraph.io.retained.StreamControl;
+import com.sun.j3d.utils.universe.SimpleUniverse;
+
+/**
+ * Writes a Java3D SceneGraph to a Java OutputStream.<p>
+ * Using this class to write to a FileOutputStream is not recommended. Use
+ * SceneGraphFileWriter instead to achieve maximum performance and flexibility.
+ */
+public class SceneGraphStreamWriter extends java.lang.Object {
+
+ private StreamControl control;
+ private DataOutputStream out;
+
+ /** Creates new SceneGraphStreamWriter that will write to the supplied stream */
+ public SceneGraphStreamWriter(java.io.OutputStream outputStream ) throws IOException {
+ this.out = new java.io.DataOutputStream( outputStream );
+ control = new StreamControl( out );
+ control.writeStreamHeader();
+ }
+
+
+ /**
+ * Write <code>universe</code> to the Stream.<p>
+ *
+ * If <code>writeContent</code> is true then all BranchGraphs attached to the
+ * universe will be saved. If it is false then only the universe
+ * data structures will be output (PlatformGeometry, ViewerAvatar, Locales,
+ * and the MultiTransformGroup between the ViewingPlatform and the View).<p>
+ *
+ * If <code>writeContent</code> is true then all the BranchGraphs
+ * attached to the Locales of the universe must have the
+ * ALLOW_DETACH capability set. If they do not, a <code>CapabilityNotSetException</code>
+ * will be thrown<p>
+ *
+ * @param universe The universe to write
+ * @param writeContent Flag enabling the BranchGraphs to be written
+ *
+ * @exception IOException
+ * @exception UnsupportedUniverseException Thrown if the universe class is not
+ * supported by this implementation
+ */
+ public void writeUniverse( SimpleUniverse universe, boolean writeContent ) throws IOException, UnsupportedUniverseException {
+ control.writeUniverse( out, universe, writeContent );
+ }
+
+ /**
+ * Write the entire graph to the stream.<p>
+ *
+ * The API will correctly handle NodeComponents that are shared
+ * between seperate graphs. However Nodes cannot be referenced
+ * in other Graphs.<p>
+ *
+ * If a reference to a Node in another graph is encountered a
+ * DanglingReferenceException will be thrown.
+ *
+ * <code>namedObjects</code> can contain a mapping between a key and a SceneGraphObject
+ * in the graph. During the read process this can be used to locate nodes
+ * in the graph.
+ */
+ public void writeBranchGraph( BranchGroup graph, HashMap namedObjects ) throws IOException, DanglingReferenceException, NamedObjectException {
+ // TODO Add namedObjects to SymbolTable
+ control.addNamedObjects( namedObjects );
+ control.writeBranchGraph( graph, null );
+ }
+
+ /**
+ * Close the SceneGraphStreamWriter, but does not close the Stream
+ */
+ public void close() throws IOException {
+ control.close();
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/UnresolvedBehavior.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/UnresolvedBehavior.java
new file mode 100644
index 0000000..6d152f2
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/UnresolvedBehavior.java
@@ -0,0 +1,64 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io;
+
+/**
+ * This Behavior is used in place of any behaviors which can not
+ * be instantiated when a scene graph is read. This behavior is
+ * always disabled when it initalizes. It just provides an indicator
+ * in the scene graph that a Behavior is missing.
+ *
+ * This normally means the Behavior is not present in the classpath.
+ */
+public class UnresolvedBehavior extends javax.media.j3d.Behavior {
+
+ public void initialize() {
+ setEnable(false);
+ }
+
+ public void processStimulus(java.util.Enumeration enumeration) {
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/UnsupportedUniverseException.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/UnsupportedUniverseException.java
new file mode 100644
index 0000000..72ba99e
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/UnsupportedUniverseException.java
@@ -0,0 +1,71 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io;
+
+/**
+ * Thrown if the VirtualUniverse subclass is not supported
+ * by the writeUniverse calls.
+ *
+ * Currently only com.sun.j3d.utils.universe.SimpleUniverse is supported
+ */
+public class UnsupportedUniverseException extends java.lang.Exception {
+
+ /**
+ * Creates new <code>UnsupportedUniverseException</code> without detail message.
+ */
+ public UnsupportedUniverseException() {
+ }
+
+
+ /**
+ * Constructs an <code>UnsupportedUniverseException</code> with the specified detail message.
+ * @param msg the detail message.
+ */
+ public UnsupportedUniverseException(String msg) {
+ super(msg);
+ }
+}
+
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/doc-files/extensibility.html b/src/classes/share/com/sun/j3d/utils/scenegraph/io/doc-files/extensibility.html
new file mode 100644
index 0000000..f92b1d9
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/doc-files/extensibility.html
@@ -0,0 +1,189 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+
+<!--
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+ -->
+
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=iso-8859-1">
+ <TITLE>Java 3D scenegraph.io Extensibility</TITLE>
+</HEAD>
+<BODY BGCOLOR="#ffffff">
+<h1>
+Using your own Classes with scenegraph.io
+</h1>
+<P>The scenegraph.io APIs will handle the IO for all the core Java3D
+SceneGraphObjects. However, if you create a subclass of one of these
+objects and add it to your Scene Graph, the IO system, by default,
+will not store any state information specific to your class.</P>
+<P>The default behavior when an unrecognized SceneGraphObject class
+is encountered is to traverse up the superclasses of the object until
+a recognized Java3D class is located. The data structures for this
+class are then used for IO. The system does store the class name of
+the original object.
+<P>For example:
+<pre><font size=-1>
+public class MyBranchGroup extends javax.media.j3d.BranchGroup {
+ private int myData;
+ ....
+}
+</font></pre>
+<P>When the Scene Graph is written to a file and this node is
+encountered, the superclass javax.media.j3d.BranchGroup will be used
+to store all the state for the object so any children of
+MyBranchGroup, the capabilities, etc. will be stored, but myData will
+be lost. When the scene graph is loaded, MyBranchGroup will be
+instantiated and will be populated with all the state from
+BranchGroup but myData will have been lost.</P>
+<P>To overcome this, the scenegraph.io API provides an interface for
+you to implement in your own classes that provides the opportunity
+for you to save the state of your classes during the IO processes.
+This is the SceneGraphIO interface.</P>
+<P>When the scenegraph is saved, the methods of SceneGraphIO are
+called in this order
+</P>
+<OL>
+ <LI><P>createSceneGraphObjectReferences</P>
+ <LI><P>saveChildren</P>
+ <LI><P>writeSceneGraphObject</P>
+</OL>
+<P>During the load cycle the method call order is</P>
+<OL>
+ <LI><P>Instantiate Object using default constructor</P>
+ <LI><P>Populate object with state from superclasses</P>
+ <LI><P>readSceneGraphObject</P>
+ <LI><P>restoreSceneGraphObjectReferences</P>
+</OL>
+<P>Within each method you need to perform the following actions:
+<UL>
+ <LI><P><b>createSceneGraphObjectReferences</b> If your object has
+ references to other SceneGraphObjects then you need to obtain an
+ object reference (int) for each reference using the
+ SceneGraphReferenceControl object passed as a parameter to this
+ method. If you don't have references to other SceneGraphObjects then
+ no action is required.</P>
+ <LI><P><b>saveChildren</b> If your object is a subclass of Group and you
+ want the scenegraph.io package to save the children then this must
+ return true. If it returns false, the object will be saved but not
+ its children.</P>
+ <LI><P><b>writeSceneGraphObject</b> In this method you must write all the
+ state information for your class, including the object references
+ obtained in createSceneGraphObjectReferences, to the DataOutput
+ stream passed into this method.</P>
+ <LI><P><b>readSceneGraphObject</b> By the time this method is called your
+ class has been instantiated and the state information in the Java3D
+ superclass will have been loaded. You should load all the state
+ information you saved for your class.</P>
+ <LI><P><b>restoreSceneGraphObjectReferences</b> is called once all the
+ SceneGraph objects have been loaded and allows you to restore the
+ references to the other SceneGraph objects.</P>
+</UL>
+<P>Here are some examples. Only the parts of the source pertaining to
+IO are show....</P>
+<h2>Behavior Example</h2>
+<pre><blockquote><font size=-1>
+public class BehaviorIO extends javax.media.j3d.Behavior implements SceneGraphIO
+ private TransformGroup target; // The TG on which this behavior acts
+ private int targetRef; // Object Reference for target
+
+ public void createSceneGraphObjectReferences( SceneGraphObjectReferenceControl ref ) {
+ targetRef = ref.addReference( target );
+ }
+
+ public void restoreSceneGraphObjectReferences( SceneGraphObjectReferenceControl ref ) {
+ target = (TransformGroup)ref.resolveReference( targetRef );
+ }
+
+ public void writeSceneGraphObject( java.io.DataOutput out ) throws IOException {
+ out.writeInt( targetRef );
+ }
+
+ public void readSceneGraphObject( java.io.DataInput in ) throws IOException {
+ targetRef = in.readInt();
+ }
+
+ // This has no effect as this is not a subclass of Group
+ public boolean saveChildren() {
+ return true;
+ }
+</font></blockquote></pre>
+<h2>
+`BlackBox' Group Example
+</h2>
+This example is a Group node that creates its subgraph during
+its instantiation. An example where you might use this is to
+represent some geometry that is loaded from an external file format
+such a OpenFLT.
+<blockquote><pre><font size=-1>
+public class House extends Group implements SceneGraphIO {
+ public House() {
+ super();
+ this.addChild( OpenFlightLoader.load( &quot;/dir/house.flt&quot; );
+ }
+
+ public void createSceneGraphObjectReferences( SceneGraphObjectReferenceControl ref ) {
+ // No references
+ }
+
+ public void restoreSceneGraphObjectReferences( SceneGraphObjectReferenceControl ref ) {
+ // No references
+ }
+
+ public void writeSceneGraphObject( java.io.DataOutput out ) throws IOException {
+ // No local state
+ }
+
+ public void readSceneGraphObject( java.io.DataInput in ) throws IOException {
+ // No local state
+ }
+
+ public boolean saveChildren() {
+ // Don't save the children as they will be restored by the openflightloader
+ return false;
+ }
+</font></blockquote></pre>
+</BODY>
+</HTML>
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/package.html b/src/classes/share/com/sun/j3d/utils/scenegraph/io/package.html
new file mode 100644
index 0000000..2cbc2ac
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/package.html
@@ -0,0 +1,105 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+
+<!--
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+ -->
+
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=iso-8859-1">
+ <TITLE></TITLE>
+</HEAD>
+<BODY>
+<P>This package provides a Java3D SceneGraph IO capability.
+The API supports IO of a scenegraph to and from a Java Stream and/or
+RandomAccessFile. The features offered for these two io systems are
+somewhat different.</P>
+<P>The SceneGraphFileReader and SceneGraphFileWriter classes provide
+IO to and from a RandomAccessFile. They allow a universe and/or
+multiple BranchGraphs to be written to the file with Node's and
+NodeComponent's shared between the separate graphs. The graphs can be
+read in any order.</P>
+<P>SceneGraphStreamReader and SceneGraphStreamWriter classes provide
+IO to and from a Stream. These classes allow a universe and/or
+multiple BranchGraphs to be passed over stream. In contrast to the
+FileReader/Writer sharing of Node's is NOT supported between graphs
+by the API. Sharing of node components is supported. If your
+application requires references to Nodes in other graphs (such as
+SharedGroups) the application must handle the references using the
+namedObjects constructs.</P>
+<P>Note : If you use SceneGraphStreamWriter class to write to a
+FileOutputStream the resulting file cannot be read using the
+SceneGraphFileReader, the converse is also true, you can not use a
+FileInputStream to load a file written by SceneGraphFileWriter.
+<P>
+The package supports the IO of all the Java3D 1.3 core classes
+and many of the utilities. It also includes interfaces which can be
+implemented to allow user defined subclasses of SceneGraphObjects to
+be stored. Information on the extensibility can be found
+<A HREF="doc-files/extensibility.html">here</A>
+<P>
+The package has a number of properties which can be used to control the IO
+behavior<p>
+
+<blockquote>
+<b>j3d.io.UseSuperClassIfNoChildClass</b> when this property is present the load
+operation will attempt to avoid failure if Scene Graph nodes are not present
+in the classpath. For example if a developer has subclassed BranchGroup with a
+class called MyBG but has not
+implemented the SceneGraphIO interface when the API saves the graph the
+superclass (ie BranchGroup) data will be stored. When the scene is loaded
+normally MyBG must be in the classpath otherwise the load will fail. If this
+property is set then the superclass node (ie BranchGroup) will be instantiated
+when MyBG is missing. Obviously, if MyBG contained any state information
+then this will be lost.<p>
+
+<b>j3d.io.ImageCompression</b> this can be set to None, GZIP, JPEG and tells the
+IO system to compress images in the .j3f file using the prescribed technique. In
+the future this will be extended to support all the formats available in
+javax.imageio in JDK 1.4.
+</blockquote>
+</P>
+</BODY>
+</HTML>
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/Controller.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/Controller.java
new file mode 100644
index 0000000..196ec41
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/Controller.java
@@ -0,0 +1,943 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.retained;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.io.DataOutput;
+import java.io.DataInput;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ObjectInputStream;
+import java.io.IOException;
+import java.util.ListIterator;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.Iterator;
+
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.BranchGroup;
+import javax.media.j3d.SharedGroup;
+import javax.media.j3d.Bounds;
+import javax.media.j3d.CapabilityNotSetException;
+import javax.media.j3d.BoundingBox;
+import javax.media.j3d.BoundingSphere;
+import javax.media.j3d.BoundingPolytope;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.Canvas3D;
+import javax.vecmath.*;
+import com.sun.j3d.utils.universe.SimpleUniverse;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.SceneGraphObjectState;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.NullSceneGraphObjectState;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.*;
+import com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.universe.SimpleUniverseState;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.ImageComponentState;
+import com.sun.j3d.utils.scenegraph.io.UnsupportedUniverseException;
+import com.sun.j3d.utils.scenegraph.io.NamedObjectException;
+import com.sun.j3d.utils.scenegraph.io.ObjectNotLoadedException;
+import com.sun.j3d.utils.universe.SimpleUniverse;
+import com.sun.j3d.utils.universe.ConfiguredUniverse;
+
+/**
+ * Provides code to control the reading and writing of Java3D objects to and
+ * from any Java IO mechanism.
+ */
+public abstract class Controller extends java.lang.Object {
+
+
+ protected static final long SYMBOL_TABLE_PTR = 30; // long - 8 bytes
+ protected static final long BG_DIR_PTR = 38; // long - 8 bytes
+ protected static final long NAMES_OBJECTS_TABLE_PTR = 46; // long - 8 bytes
+ protected static final long NODE_TYPES_PTR = 52; // long - 8 bytes
+ protected static final long UNIVERSE_CONFIG_PTR = 60; // long - 8 bytes
+ protected static final long BRANCH_GRAPH_COUNT = 68; // int - 4 bytes
+ protected static final long FILE_DESCRIPTION = 72; // UTF - n bytes
+
+ protected SymbolTable symbolTable;
+ protected NullSceneGraphObjectState nullObject = new NullSceneGraphObjectState( null, this );
+
+ /**
+ * The currentFileVersion being read
+ */
+ protected int currentFileVersion;
+
+ /**
+ * The File version which will be written
+ *
+ * 1 = Java3D 1.3 beta 1
+ * 2 = Java3D 1.3 FCS, 1) fix to allow skipping user data written via
+ SceneGraphIO interface
+ 2) Add missing duplicateOnCloneTree flag
+ (bug 4690159)
+ */
+ protected int outputFileVersion = 2;
+
+ protected ClassLoader classLoader = ClassLoader.getSystemClassLoader();
+
+ /**
+ * If true when loading a scenegraph that contains nodes who's classes
+ * are not in the classpath then use then first Java3D core superclass
+ * to instantiate the node.
+ *
+ * If false a SGIORuntimeException will be thrown when classes cannot be
+ * located
+ */
+ private boolean useSuperClass = false;
+
+ private int imageCompression = ImageComponentState.JPEG_COMPRESSION;
+
+ /** Creates new Controller */
+ public Controller() {
+ try {
+ if ( System.getProperty("j3d.io.UseSuperClassIfNoChildClass")!=null)
+ useSuperClass = true;
+
+ String imageC = System.getProperty("j3d.io.ImageCompression");
+ if (imageC!=null) {
+ if (imageC.equalsIgnoreCase("None"))
+ imageCompression = ImageComponentState.NO_COMPRESSION;
+ else if (imageC.equalsIgnoreCase("GZIP"))
+ imageCompression = ImageComponentState.GZIP_COMPRESSION;
+ else if (imageC.equalsIgnoreCase("JPEG"))
+ imageCompression = ImageComponentState.JPEG_COMPRESSION;
+ }
+ } catch( Exception e ) {}
+
+ }
+
+ public final SymbolTable getSymbolTable() {
+ return symbolTable;
+ }
+
+ /**
+ * Get the file version that we should write
+ */
+ public int getOutputFileVersion() {
+ return outputFileVersion;
+ }
+
+ /**
+ * Get the file version of the file we are reading
+ */
+ public int getCurrentFileVersion() {
+ return currentFileVersion;
+ }
+
+ /**
+ * Create a new state object and check for a pre-existing symbol table
+ * entry
+ */
+ public SceneGraphObjectState createState( SceneGraphObject obj ) {
+ return createState( obj, symbolTable.getSymbol( obj ) );
+ }
+
+ /**
+ * Given a scene graph object instantiate the correct State class
+ * for that object. If the symbol already exists (is not null) then
+ * increment the reference count, otherwise create a new symbol.
+ */
+ public SceneGraphObjectState createState( SceneGraphObject obj, SymbolTableData symbol ) {
+ if (obj==null) return nullObject;
+
+ if (symbol!=null) {
+ symbol.incrementReferenceCount();
+ symbolTable.setBranchGraphID( symbol );
+ if (symbol.getNodeState()!=null)
+ return symbol.getNodeState();
+ } else
+ symbol = symbolTable.createSymbol( obj );
+
+ return createState( symbol );
+ }
+
+ /**
+ * Return the state class for the SceneGraphObject, creating one if it does
+ * not already exist
+ */
+ public SceneGraphObjectState createState( SymbolTableData symbol ) {
+ SceneGraphObject obj = symbol.getJ3dNode();
+ if (obj==null) return nullObject;
+
+ String name = obj.getClass().getName();
+ SceneGraphObjectState ret;
+
+ try {
+ Class state = Class.forName( "com.sun.j3d.utils.scenegraph.io.state."+name+"State", true, classLoader );
+ ret = constructStateObj( symbol, state, obj.getClass() );
+ } catch(ClassNotFoundException e) {
+ ret = checkSuperClasses( symbol );
+ if (!(obj instanceof com.sun.j3d.utils.scenegraph.io.SceneGraphIO))
+ System.out.println("Could not find "+"com.sun.j3d.utils.scenegraph.io.state."+name+"State, using superclass "+ret.getClass().getName() );
+ if (ret==null)
+ throw new SGIORuntimeException( "No State class for "+
+ obj.getClass().getName() );
+ }
+
+ symbol.nodeState = ret;
+
+ return ret;
+ }
+ private SceneGraphObjectState constructStateObj( SymbolTableData symbol,
+ Class state,
+ Class objClass ) {
+
+ SceneGraphObjectState ret = null;
+
+ try {
+ Constructor construct = state.getConstructor(
+ new Class[] { com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData.class,
+ com.sun.j3d.utils.scenegraph.io.retained.Controller.class
+ } );
+ ret = (SceneGraphObjectState)construct.newInstance(
+ new Object[]{ symbol, this } );
+
+ } catch( NoSuchMethodException ex ) {
+ System.out.println("Looking for Constructor ("+symbol.j3dNode.getClass().getName()+", Controller )");
+ throw new SGIORuntimeException( "1 Broken State class for "+
+ state.getName() );
+ } catch( InvocationTargetException exc ) {
+ exc.printStackTrace();
+ throw new SGIORuntimeException( "2 Broken State class for "+
+ state.getName() );
+ } catch( IllegalAccessException exce ) {
+ throw new SGIORuntimeException( "3 Broken State class for "+
+ state.getName() );
+ } catch( InstantiationException excep ) {
+ throw new SGIORuntimeException( "4 Broken State class for "+
+ state.getName() );
+ }
+
+ return ret;
+ }
+
+ /**
+ * Check to see if any of the superclasses of obj are
+ * known to the Java3D IO package
+ */
+ private SceneGraphObjectState checkSuperClasses( SymbolTableData symbol ) {
+
+ Class cl = symbol.j3dNode.getClass().getSuperclass();
+ Class state = null;
+ boolean finished = false;
+
+
+ while( cl != null & !finished ) {
+ String name = cl.getName();
+ //System.out.println("Got superclass "+name);
+ try {
+ state = Class.forName( "com.sun.j3d.utils.scenegraph.io.state."+name+"State", true, classLoader );
+ } catch(ClassNotFoundException e) {
+ state = null;
+ }
+
+ if (state!=null)
+ finished = true;
+ else
+ cl = cl.getSuperclass();
+ }
+
+ if (cl==null)
+ throw new SGIORuntimeException( "Unsupported class "+symbol.j3dNode.getClass().getName() );
+
+ return constructStateObj( symbol, state, cl );
+ }
+
+
+ public void writeObject( DataOutput out, SceneGraphObjectState obj ) throws IOException {
+
+ int classID = getStateID( obj );
+
+ out.writeInt( classID ); // Node class id
+
+ if (classID==0) {
+ out.writeUTF( obj.getClass().getName() );
+ }
+
+ obj.writeObject( out );
+ }
+
+ public SceneGraphObjectState readObject( DataInput in ) throws IOException {
+ int classID = in.readInt();
+
+ SceneGraphObjectState state = null;
+
+ if (classID==-1)
+ return nullObject;
+ else if (classID==0) {
+ String stateClassName = in.readUTF();
+
+ try {
+ Class cl = Class.forName( stateClassName, true, classLoader );
+ // System.out.println("Got class "+cl );
+ Constructor construct = cl.getConstructor(
+ new Class[] {
+ com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData.class,
+ com.sun.j3d.utils.scenegraph.io.retained.Controller.class} );
+
+ // System.out.println("Got constructor "+construct );
+ state = (SceneGraphObjectState)construct.newInstance(
+ new Object[]{ null, this } );
+
+ // System.out.println("Got state instance "+state);
+ } catch(ClassNotFoundException e) {
+ throw new java.io.IOException( "Error Loading State Class "+stateClassName+" "+e.getMessage() );
+ } catch( NoSuchMethodException ex ) {
+ throw new java.io.IOException( "1 Broken State class for "+
+ stateClassName+" "+ex.getMessage() );
+ } catch( InvocationTargetException exc ) {
+ exc.printStackTrace();
+ throw new java.io.IOException( "2 Broken State class for "+
+ stateClassName );
+ } catch( IllegalAccessException exce ) {
+ throw new java.io.IOException( "3 Broken State class for "+
+ stateClassName );
+ } catch( InstantiationException excep ) {
+ throw new java.io.IOException( "4 Broken State class for "+
+ stateClassName );
+ }
+ } else {
+ state = createCoreState( classID );
+ }
+
+ state.readObject( in );
+
+ return state;
+ }
+
+ /**
+ * Set the class loader used to load the Scene Graph Objects and
+ * the serialized user data. The default is
+ * ClassLoader.getSystemClassLoader()
+ */
+ public void setClassLoader( ClassLoader classLoader ) {
+ this.classLoader = classLoader;
+ }
+
+
+ /**
+ * Get the class loader used to load the Scene Graph Objects and
+ * the serialized user data. The default is
+ * ClassLoader.getSystemClassLoader()
+ */
+ public ClassLoader getClassLoader() {
+ return classLoader;
+ }
+
+ /**
+ * Write all the unsaved NodeComponents and SharedGroups to DataOutput.
+ * Mark all the NodeComponents as saved.
+ */
+ protected void writeNodeComponents( DataOutput out ) throws IOException {
+ // This method is overridden by RandomAccessFileControl
+ // The RandomAccessFileControl version sets the pointer to
+ // the next NodeComponent correclty
+
+ ListIterator list = symbolTable.getUnsavedNodeComponents();
+ out.writeInt( symbolTable.getUnsavedNodeComponentsSize() );
+ while( list.hasNext() ) {
+ SymbolTableData symbol = (SymbolTableData)list.next();
+
+ out.writeInt( symbol.nodeID );
+ out.writeLong( 0L ); // Pointer to next NodeComponent
+
+ writeObject( out, symbol.getNodeState() );
+ }
+ }
+
+ /**
+ * Read in all the node components in this block
+ */
+ protected void readNodeComponents( DataInput in ) throws IOException {
+ int count = in.readInt();
+
+ for(int i=0; i<count; i++) {
+ // nodeID and nextNC data is used in RandomAccessFileControl
+ // version of readNodeComponents
+ int nodeID = in.readInt();
+ long nextNC = in.readLong();
+
+ SceneGraphObjectState nodeComponent = readObject( in );
+ }
+ }
+
+ /**
+ * Write the shared group and it's node components to the IO stream
+ */
+ public void writeSharedGroup( DataOutput out, SharedGroup sharedGroup, SymbolTableData symbol ) throws IOException {
+ SceneGraphObjectState state = createState( sharedGroup, symbol );
+ symbolTable.startUnsavedNodeComponentFrame();
+ writeObject( out, state );
+ writeNodeComponents( out );
+ symbolTable.endUnsavedNodeComponentFrame();
+ }
+
+ /**
+ * Read a Shared group and it's node components from the IO Stream
+ */
+ public int readSharedGroup( DataInput in ) throws IOException {
+ SceneGraphObjectState state = readObject( in );
+ readNodeComponents( in );
+
+ return state.getNodeID();
+ }
+
+ /**
+ * Write out the Universe information.
+ */
+ public void writeUniverse( DataOutput out, SimpleUniverse universe,
+ boolean writeUniverseContent ) throws IOException, UnsupportedUniverseException, CapabilityNotSetException {
+ if (universe==null) {
+ out.writeUTF( "null" );
+ } else if ( universe instanceof SimpleUniverse ) {
+ out.writeUTF( universe.getClass().getName() );
+ SimpleUniverseState state = new SimpleUniverseState( universe, this );
+ state.writeObject( out );
+
+ if (writeUniverseContent) {
+ state.detachAllGraphs();
+ int[] graphs = state.getAllGraphIDs();
+ for(int i=0; i<graphs.length; i++) {
+ SymbolTableData symbol = symbolTable.getBranchGraphRoot( graphs[i] );
+ System.out.println("Writing "+graphs[i]+" "+symbol.j3dNode );
+ writeBranchGraph( (BranchGroup)symbol.j3dNode, null );
+ }
+
+ state.attachAllGraphs();
+ }
+ } else {
+ throw new UnsupportedUniverseException(
+ "Current Implementation only support SimpleUniverse/ConfiguredUniverse.");
+ }
+ }
+
+ /**
+ * Read and create a new Universe matching the one used during save.
+ *
+ * @param attachBranchGraphs If true then all the branchGraph attached to
+ * the universe when it was saved will be loaded and reattached.
+ */
+ public ConfiguredUniverse readUniverse(DataInput in, boolean attachBranchGraphs,
+ Canvas3D canvas) throws IOException {
+ String universeClass = in.readUTF();
+ //System.out.println(universeClass);
+ if (universeClass.equals("null"))
+ return null;
+ else if ( (universeClass.equals("com.sun.j3d.utils.universe.SimpleUniverse")) ||
+ (universeClass.equals("com.sun.j3d.utils.universe.ConfiguredUniverse")) ) {
+ SimpleUniverseState state = new SimpleUniverseState( this );
+ state.readObject( in, canvas );
+
+ if (attachBranchGraphs) {
+ int[] graphs = state.getAllGraphIDs();
+ readBranchGraphs( graphs );
+
+ state.buildGraph();
+ }
+
+ return state.getNode();
+ }
+ throw new IOException("Unrecognized universe class "+universeClass);
+ }
+
+ /**
+ * Read the set of branchgraps.
+ *
+ * Used by readUniverse
+ *
+ * RandomAccessFileControl will read the graphs in the array,
+ * StreamControl will read all graphs in the stream
+ */
+ protected abstract void readBranchGraphs( int[] graphs ) throws IOException;
+
+ public abstract void writeBranchGraph( BranchGroup bg, java.io.Serializable userData) throws IOException;
+
+ /**
+ * Reset the controller, ready to load/save data to a new file
+ */
+ public void reset() {
+ symbolTable.clear();
+ }
+
+ /**
+ * 'Core' classes (ie those hard coded in this API) are assigned a
+ * numerical value representing their class. This simply saves space
+ * and IO bandwidth
+ */
+ private SceneGraphObjectState createCoreState( int classID ) {
+
+ if (classID==-1)
+ return nullObject;
+ else if (classID==0)
+ return null;
+
+ Class j3dClass = getNodeClassFromID( classID-1 );
+ String j3dClassName = j3dClass.getName();
+ String stateClassName = "com.sun.j3d.utils.scenegraph.io.state."+j3dClassName+"State";
+
+ SceneGraphObjectState stateObj = null;
+ try {
+ Class stateClass = Class.forName( stateClassName, true, ClassLoader.getSystemClassLoader() );
+ Constructor stateConstructor = stateClass.getConstructor( new Class[] { SymbolTableData.class, Controller.class } );
+ stateObj = (SceneGraphObjectState)stateConstructor.newInstance( new Object[] { null, this } );
+ } catch( Exception e ) {
+ e.printStackTrace();
+ }
+
+ return stateObj;
+ }
+
+ /**
+ * Return the id of the state class
+ */
+ private int getStateID( SceneGraphObjectState state ) {
+
+ if (state instanceof NullSceneGraphObjectState)
+ return -1;
+
+ return getNodeClassID( state.getNode() )+1;
+ }
+
+ // The order of this array dictates the ID's of classes therefore
+ // changing the order of this array will break backward compatability
+ Class[] j3dClasses = new Class[] {
+ javax.media.j3d.Alpha.class,
+ javax.media.j3d.Appearance.class,
+ javax.media.j3d.Billboard.class,
+ javax.media.j3d.BranchGroup.class,
+ javax.media.j3d.ColoringAttributes.class,
+ javax.media.j3d.ConeSound.class,
+ javax.media.j3d.DecalGroup.class,
+ javax.media.j3d.DirectionalLight.class,
+ javax.media.j3d.DistanceLOD.class,
+ javax.media.j3d.ExponentialFog.class,
+ javax.media.j3d.Font3D.class,
+ javax.media.j3d.Group.class,
+ javax.media.j3d.ImageComponent2D.class,
+ javax.media.j3d.ImageComponent3D.class,
+ javax.media.j3d.IndexedLineArray.class,
+ javax.media.j3d.IndexedLineStripArray.class,
+ javax.media.j3d.IndexedPointArray.class,
+ javax.media.j3d.IndexedQuadArray.class,
+ javax.media.j3d.IndexedTriangleArray.class,
+ javax.media.j3d.IndexedTriangleFanArray.class,
+ javax.media.j3d.IndexedTriangleStripArray.class,
+ javax.media.j3d.LinearFog.class,
+ javax.media.j3d.LineArray.class,
+ javax.media.j3d.LineAttributes.class,
+ javax.media.j3d.LineStripArray.class,
+ javax.media.j3d.Link.class,
+ javax.media.j3d.Material.class,
+ javax.media.j3d.Morph.class,
+ javax.media.j3d.OrderedGroup.class,
+ javax.media.j3d.OrientedShape3D.class,
+ javax.media.j3d.PathInterpolator.class,
+ javax.media.j3d.PointArray.class,
+ javax.media.j3d.PointAttributes.class,
+ javax.media.j3d.PositionInterpolator.class,
+ javax.media.j3d.PositionPathInterpolator.class,
+ javax.media.j3d.QuadArray.class,
+ javax.media.j3d.RenderingAttributes.class,
+ javax.media.j3d.RotationInterpolator.class,
+ javax.media.j3d.RotationPathInterpolator.class,
+ javax.media.j3d.RotPosPathInterpolator.class,
+ javax.media.j3d.RotPosScalePathInterpolator.class,
+ javax.media.j3d.ScaleInterpolator.class,
+ javax.media.j3d.Shape3D.class,
+ javax.media.j3d.SharedGroup.class,
+ javax.media.j3d.Soundscape.class,
+ javax.media.j3d.SpotLight.class,
+ javax.media.j3d.Switch.class,
+ javax.media.j3d.SwitchValueInterpolator.class,
+ javax.media.j3d.Text3D.class,
+ javax.media.j3d.Texture2D.class,
+ javax.media.j3d.Texture3D.class,
+ javax.media.j3d.TextureAttributes.class,
+ javax.media.j3d.TextureCubeMap.class,
+ javax.media.j3d.TextureUnitState.class,
+ javax.media.j3d.TransformGroup.class,
+ javax.media.j3d.TransformInterpolator.class,
+ javax.media.j3d.TransparencyAttributes.class,
+ javax.media.j3d.TransparencyInterpolator.class,
+ javax.media.j3d.TriangleArray.class,
+ javax.media.j3d.TriangleFanArray.class,
+ javax.media.j3d.TriangleStripArray.class,
+ javax.media.j3d.ViewPlatform.class
+ };
+
+ public Class getNodeClassFromID( int classID ) {
+ if (classID<0)
+ return null;
+ else
+ return j3dClasses[classID];
+ }
+
+ // TODO Use a HashMap to eliminate the linear search for the class
+ //
+ public int getNodeClassID( javax.media.j3d.SceneGraphObject node ) {
+
+ int ret = -1;
+ Class cl = node.getClass();
+
+ for(int i=0; i<j3dClasses.length && ret==-1; i++)
+ if (j3dClasses[i]==cl)
+ ret = i;
+
+ return ret;
+ }
+
+ /**
+ * Associate the name with the scene graph object
+ */
+ public void addNamedObject( String name, SceneGraphObject object ) {
+ symbolTable.addNamedObject( name, object );
+ }
+
+ /**
+ * Return the SceneGraphObject associated with the name
+ */
+ public SceneGraphObject getNamedObject( String name ) throws NamedObjectException, ObjectNotLoadedException {
+ return symbolTable.getNamedObject( name );
+ }
+
+ /**
+ * Get all the names of the named objects
+ */
+ public String[] getNames() {
+ return symbolTable.getNames();
+ }
+
+ /**
+ * Write a serializable object to the current file position, proceeded by
+ * the size of the object
+ */
+ public void writeSerializedData( DataOutput dataOutput, java.io.Serializable userData ) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ObjectOutputStream objOut = new ObjectOutputStream( out );
+
+ objOut.writeObject( userData );
+
+ out.close();
+
+ byte[] bytes = out.toByteArray();
+ dataOutput.writeInt( bytes.length );
+ if (bytes.length!=0)
+ dataOutput.write( bytes );
+ }
+
+ public Object readSerializedData( DataInput dataInput ) throws IOException {
+ int size = dataInput.readInt();
+ Object userData = null;
+
+ if (size!=0) {
+ byte[] bytes = new byte[size];
+ dataInput.readFully( bytes );
+
+ ByteArrayInputStream in = new ByteArrayInputStream( bytes );
+ J3dIOObjectInputStream objIn = new J3dIOObjectInputStream( in );
+
+ try {
+ userData = objIn.readObject();
+ objIn.close();
+ } catch( ClassNotFoundException e ) {
+ System.out.println("WARNING: Unable to load UserData");
+ System.out.println("Class missing "+e);
+ objIn.close();
+ }
+ }
+
+ return userData;
+ }
+
+ /**
+ * Skip past the user data object
+ */
+ public void skipUserData( DataInput dataInput ) throws IOException {
+ int size = dataInput.readInt();
+ dataInput.skipBytes( size );
+ }
+
+
+
+ public void writeColor3f( DataOutput out, Color3f color ) throws IOException {
+ out.writeFloat( color.x );
+ out.writeFloat( color.y );
+ out.writeFloat( color.z );
+ }
+
+ public Color3f readColor3f( DataInput in ) throws IOException {
+ return new Color3f( in.readFloat(), in.readFloat(), in.readFloat() );
+ }
+
+ public void writeColor4f( DataOutput out, Color4f vec ) throws IOException {
+ writeTuple4f( out, vec );
+ }
+
+ public Color4f readColor4f( DataInput in ) throws IOException {
+ return (Color4f)readTuple4f( in, new Color4f() );
+ }
+
+ public void writePoint3f( DataOutput out, Point3f pt ) throws IOException {
+ writeTuple3f( out, pt );
+ }
+
+ public Point3f readPoint3f( DataInput in ) throws IOException {
+ return (Point3f)readTuple3f( in, new Point3f() );
+ }
+
+ public void writePoint3d( DataOutput out, Point3d pt ) throws IOException {
+ writeTuple3d( out, pt );
+ }
+
+ public Point3d readPoint3d( DataInput in ) throws IOException {
+ return (Point3d)readTuple3d( in, new Point3d() );
+ }
+
+ public void writeVector3f( DataOutput out, Vector3f vec ) throws IOException {
+ writeTuple3f( out, vec );
+ }
+
+ public Vector3f readVector3f( DataInput in ) throws IOException {
+ return (Vector3f)readTuple3f( in, new Vector3f() );
+ }
+
+ public void writeVector4d( DataOutput out, Vector4d vec ) throws IOException {
+ writeTuple4d( out, vec );
+ }
+
+ public Vector4d readVector4d( DataInput in ) throws IOException {
+ return (Vector4d)readTuple4d( in, new Vector4d() );
+ }
+
+ public void writeVector4f( DataOutput out, Vector4f vec ) throws IOException {
+ writeTuple4f( out, vec );
+ }
+
+ public Vector4f readVector4f( DataInput in ) throws IOException {
+ return (Vector4f)readTuple4f( in, new Vector4f() );
+ }
+
+ public void writeQuat4f( DataOutput out, Quat4f vec ) throws IOException {
+ writeTuple4f( out, vec );
+ }
+
+ public Quat4f readQuat4f( DataInput in ) throws IOException {
+ return (Quat4f)readTuple4f( in, new Quat4f() );
+ }
+
+ public void writeMatrix4d( DataOutput out, Matrix4d m ) throws IOException {
+ for(int r=0; r<4; r++)
+ for(int c=0; c<4; c++)
+ out.writeDouble( m.getElement( r, c ));
+ }
+
+ public Matrix4d readMatrix4d( DataInput in ) throws IOException {
+ double elements[] = new double[16];
+ for(int c=0; c<16; c++)
+ elements[ c ] = in.readDouble();
+
+ return new Matrix4d(elements);
+ }
+
+ public void writeTuple3f( DataOutput out, Tuple3f tuple ) throws IOException {
+ out.writeFloat( tuple.x );
+ out.writeFloat( tuple.y );
+ out.writeFloat( tuple.z );
+ }
+
+ public Tuple3f readTuple3f( DataInput in, Tuple3f tuple ) throws IOException {
+ tuple.x = in.readFloat();
+ tuple.y = in.readFloat();
+ tuple.z = in.readFloat();
+ return tuple;
+ }
+
+ public void writeTuple3d( DataOutput out, Tuple3d tuple ) throws IOException {
+ out.writeDouble( tuple.x );
+ out.writeDouble( tuple.y );
+ out.writeDouble( tuple.z );
+ }
+
+ public Tuple3d readTuple3d( DataInput in, Tuple3d tuple ) throws IOException {
+ tuple.x = in.readDouble();
+ tuple.y = in.readDouble();
+ tuple.z = in.readDouble();
+ return tuple;
+ }
+
+ public void writeTuple4d( DataOutput out, Tuple4d tuple ) throws IOException {
+ out.writeDouble( tuple.x );
+ out.writeDouble( tuple.y );
+ out.writeDouble( tuple.z );
+ out.writeDouble( tuple.w );
+ }
+
+ public Tuple4d readTuple4d( DataInput in, Tuple4d tuple ) throws IOException {
+ tuple.x = in.readDouble();
+ tuple.y = in.readDouble();
+ tuple.z = in.readDouble();
+ tuple.w = in.readDouble();
+ return tuple;
+ }
+
+ public void writeTuple4f( DataOutput out, Tuple4f tuple ) throws IOException {
+ out.writeFloat( tuple.x );
+ out.writeFloat( tuple.y );
+ out.writeFloat( tuple.z );
+ out.writeFloat( tuple.w );
+ }
+
+ public Tuple4f readTuple4f( DataInput in, Tuple4f tuple ) throws IOException {
+ tuple.x = in.readFloat();
+ tuple.y = in.readFloat();
+ tuple.z = in.readFloat();
+ tuple.w = in.readFloat();
+ return tuple;
+ }
+
+ public void writeTransform3D( DataOutput out, Transform3D tran ) throws IOException {
+ Matrix4d matrix = new Matrix4d();
+ tran.get( matrix );
+ writeMatrix4d( out, matrix );
+ }
+
+ public Transform3D readTransform3D( DataInput in ) throws IOException {
+ Transform3D ret = new Transform3D();
+ ret.set( readMatrix4d( in ));
+ return ret;
+ }
+
+ public void writeBounds( DataOutput out, Bounds bounds ) throws IOException {
+ if (bounds==null) {
+ out.writeInt( 0 );
+ } else if (bounds instanceof BoundingBox) {
+ out.writeInt( 1 ); // Type
+ Point3d p = new Point3d();
+ ((BoundingBox)bounds).getLower( p );
+ writePoint3d( out, p );
+ ((BoundingBox)bounds).getUpper( p );
+ writePoint3d( out, p );
+ } else if (bounds instanceof BoundingSphere) {
+ out.writeInt( 2 ); // Type
+ Point3d p = new Point3d();
+ ((BoundingSphere)bounds).getCenter( p );
+ writePoint3d( out, p );
+ out.writeDouble( ((BoundingSphere)bounds).getRadius() );
+ } else if (bounds instanceof BoundingPolytope ) {
+ out.writeInt( 3 ); // Type
+ Vector4d[] planes = new Vector4d[ ((BoundingPolytope)bounds).getNumPlanes() ];
+ ((BoundingPolytope)bounds).getPlanes( planes );
+ out.writeInt( planes.length );
+ for(int i=0; i<planes.length; i++)
+ writeVector4d( out, planes[i] );
+ } else {
+ throw new IOException( "Unsupported bounds class "+bounds.getClass().getName() );
+ }
+ }
+
+ public Bounds readBounds( DataInput in ) throws IOException {
+ Bounds bounds;
+ switch( in.readInt() ) {
+ case 0:
+ bounds = null;
+ break;
+ case 1:
+ bounds = new BoundingBox( readPoint3d(in), readPoint3d(in) );
+ break;
+ case 2:
+ bounds = new BoundingSphere( readPoint3d(in), in.readDouble() );
+ break;
+ case 3:
+ Vector4d[] planes = new Vector4d[ in.readInt() ];
+ for(int i=0; i<planes.length; i++)
+ planes[i] = readVector4d( in );
+ bounds = new BoundingPolytope(planes);
+ break;
+ default:
+ throw new SGIORuntimeException("Unrecognised bounds class");
+ }
+ return bounds;
+ }
+
+/**
+ * Get the current file 'pointer' location.
+ */
+ public abstract long getFilePointer();
+
+ public abstract void close() throws IOException;
+
+ /**
+ * Indicates to SceneGraphObjectState that it should use the
+ * Java3D core superclass for any tree nodes whose classes are
+ * not in the classpath during a load.
+ */
+ public boolean useSuperClassIfNoChildClass() {
+ return useSuperClass;
+ }
+
+ /**
+ * Returns the imageCompression to be used
+ * IMAGE_COMPRESSION_NONE, IMAGE_COMPRESSION_GZIP, IMAGE_COMPRESSION_JPEG
+ */
+ public int getImageCompression() {
+ return imageCompression;
+ }
+
+
+ /**
+ * An ObjectInputStream that uses a different classLoader
+ */
+ class J3dIOObjectInputStream extends ObjectInputStream {
+ public J3dIOObjectInputStream( java.io.InputStream in ) throws
+ IOException {
+ super(in);
+ }
+
+ protected Class resolveClass( java.io.ObjectStreamClass desc ) throws
+ IOException, ClassNotFoundException {
+ return getClass().forName( desc.getName(), true, classLoader );
+ }
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/J3fInputStream.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/J3fInputStream.java
new file mode 100644
index 0000000..83dca36
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/J3fInputStream.java
@@ -0,0 +1,136 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.retained;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * J3fInputStream class for SceneGraph I/O.
+ */
+public class J3fInputStream implements java.io.DataInput {
+
+ private PositionInputStream positionInputStream;
+ private DataInputStream dataInputStream;
+
+ /** Creates new J3fInputStream */
+ public J3fInputStream( java.io.InputStream stream ) {
+ positionInputStream = new PositionInputStream( stream );
+ dataInputStream = new DataInputStream( positionInputStream );
+ }
+
+ /**
+ * Move the file pointer to the specified position.
+ * The position MUST be greater or equal to the current position
+ */
+ public void seekForward( long position ) throws IOException {
+ positionInputStream.seekForward( position );
+ }
+
+ public long getFilePointer() {
+ return positionInputStream.getFilePointer();
+ }
+
+ public int readUnsignedShort() throws java.io.IOException {
+ return dataInputStream.readUnsignedShort();
+ }
+
+ public void readFully(byte[] p1) throws java.io.IOException {
+ dataInputStream.readFully(p1);
+ }
+
+ public char readChar() throws java.io.IOException {
+ return dataInputStream.readChar();
+ }
+
+ public int readUnsignedByte() throws java.io.IOException {
+ return dataInputStream.readUnsignedByte();
+ }
+
+ public int readInt() throws java.io.IOException {
+ return dataInputStream.readInt();
+ }
+
+ public short readShort() throws java.io.IOException {
+ return dataInputStream.readShort();
+ }
+
+ public float readFloat() throws java.io.IOException {
+ return dataInputStream.readFloat();
+ }
+
+ public void readFully(byte[] p1,int p2,int p3) throws java.io.IOException {
+ dataInputStream.readFully( p1, p2, p3 );
+ }
+
+ public boolean readBoolean() throws java.io.IOException {
+ return dataInputStream.readBoolean();
+ }
+
+ public int skipBytes(int p1) throws java.io.IOException {
+ return dataInputStream.skipBytes(p1);
+ }
+
+ public double readDouble() throws java.io.IOException {
+ return dataInputStream.readDouble();
+ }
+
+ public long readLong() throws java.io.IOException {
+ return dataInputStream.readLong();
+ }
+
+ public java.lang.String readLine() throws java.io.IOException {
+ return dataInputStream.readLine();
+ }
+
+ public byte readByte() throws java.io.IOException {
+ return dataInputStream.readByte();
+ }
+
+ public java.lang.String readUTF() throws java.io.IOException {
+ return dataInputStream.readUTF();
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/J3fOutputStream.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/J3fOutputStream.java
new file mode 100644
index 0000000..15ce29f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/J3fOutputStream.java
@@ -0,0 +1,132 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.retained;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+/**
+ * J3fOutputStream class for SceneGraph I/O.
+ */
+public class J3fOutputStream implements java.io.DataOutput {
+
+ private PositionOutputStream positionOutputStream;
+ private DataOutputStream dataOutputStream;
+
+ /** Creates new J3fInputStream */
+ public J3fOutputStream( java.io.OutputStream stream ) {
+ positionOutputStream = new PositionOutputStream( stream );
+ dataOutputStream = new DataOutputStream( positionOutputStream );
+ }
+
+ /**
+ * Move the file pointer to the specified position.
+ * The position MUST be greater or equal to the current position
+ */
+ public void seekForward( long position ) throws IOException {
+ positionOutputStream.seekForward( position );
+ }
+
+ public long getFilePointer() {
+ return positionOutputStream.getFilePointer();
+ }
+
+ public void write(byte[] p1,int p2,int p3) throws java.io.IOException {
+ dataOutputStream.write( p1, p2, p3 );
+ }
+
+ public void writeFloat(float p1) throws java.io.IOException {
+ dataOutputStream.writeFloat(p1);
+ }
+
+ public void write(int p1) throws java.io.IOException {
+ dataOutputStream.write(p1 );
+ }
+
+ public void writeShort(int p1) throws java.io.IOException {
+ dataOutputStream.writeShort( p1 );
+ }
+
+ public void writeBytes(java.lang.String p1) throws java.io.IOException {
+ dataOutputStream.writeBytes( p1 );
+ }
+
+ public void writeChar(int p1) throws java.io.IOException {
+ dataOutputStream.writeChar( p1 );
+ }
+
+ public void writeByte(int p1) throws java.io.IOException {
+ dataOutputStream.writeByte( p1 );
+ }
+
+ public void writeLong(long p1) throws java.io.IOException {
+ dataOutputStream.writeLong( p1 );
+ }
+
+ public void writeBoolean(boolean p1) throws java.io.IOException {
+ dataOutputStream.writeBoolean( p1 );
+ }
+
+ public void writeUTF(java.lang.String p1) throws java.io.IOException {
+ dataOutputStream.writeUTF( p1 );
+ }
+
+ public void writeInt(int p1) throws java.io.IOException {
+ dataOutputStream.writeInt( p1 );
+ }
+
+ public void writeChars(java.lang.String p1) throws java.io.IOException {
+ dataOutputStream.writeChars( p1 );
+ }
+
+ public void write(byte[] p1) throws java.io.IOException {
+ dataOutputStream.write( p1 );
+ }
+
+ public void writeDouble(double p1) throws java.io.IOException {
+ dataOutputStream.writeDouble( p1 );
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/PositionInputStream.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/PositionInputStream.java
new file mode 100644
index 0000000..45fa8c3
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/PositionInputStream.java
@@ -0,0 +1,98 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.retained;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+class PositionInputStream extends java.io.InputStream {
+
+ private long pos = 0;
+ private java.io.InputStream stream;
+
+ public PositionInputStream( java.io.InputStream stream ) {
+ this.stream = stream;
+ }
+
+ public int read() throws IOException {
+ pos++;
+ return stream.read();
+ }
+
+ public int read( byte[] b ) throws IOException {
+ int s = stream.read( b );
+ pos += s;
+ return s;
+ }
+
+ public int read( byte[] b, int off, int len ) throws IOException {
+ int s = stream.read( b, off, len );
+ pos += s;
+ return s;
+ }
+
+ public long skip( long n ) throws IOException {
+ long s = stream.skip( n );
+ pos += s;
+ return s;
+ }
+
+ /**
+ * Move the file pointer to the specified position.
+ * The position MUST be greater or equal to the current position
+ */
+ public void seekForward( long position ) throws IOException {
+ if (pos>position)
+ throw new SGIORuntimeException( "Seeking Backward "+pos +" "+position );
+ else
+ stream.skip( (int)(position-pos) );
+
+ pos = position;
+ }
+
+ public long getFilePointer() {
+ return pos;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/PositionOutputStream.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/PositionOutputStream.java
new file mode 100644
index 0000000..db55bf1
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/PositionOutputStream.java
@@ -0,0 +1,91 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.retained;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+class PositionOutputStream extends java.io.OutputStream {
+
+ private long pos = 0;
+ private java.io.OutputStream stream;
+
+ public PositionOutputStream( java.io.OutputStream stream ) {
+ this.stream = stream;
+ }
+
+ public void write(int p1) throws IOException {
+ pos++;
+ stream.write(p1);
+ }
+
+ public void write( byte[] b ) throws IOException {
+ pos+= b.length;
+ stream.write( b );
+ }
+
+ public void write( byte[] b, int off, int len ) throws IOException {
+ pos+= len;
+ stream.write( b, off, len );
+ }
+
+ /**
+ * Move the file pointer to the specified position.
+ * The position MUST be greater or equal to the current position
+ */
+ public void seekForward( long position ) throws IOException {
+ if (pos>position)
+ throw new SGIORuntimeException( "Seeking Backward "+pos +" "+position );
+ else
+ for(int i=0; i< (int)(position-pos); i++)
+ stream.write(0);
+
+ pos = position;
+ }
+
+ public long getFilePointer() {
+ return pos;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/RandomAccessFileControl.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/RandomAccessFileControl.java
new file mode 100644
index 0000000..003a74e
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/RandomAccessFileControl.java
@@ -0,0 +1,500 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.retained;
+
+import java.io.RandomAccessFile;
+import java.io.IOException;
+import java.io.DataOutput;
+import java.io.DataInput;
+
+import javax.media.j3d.BranchGroup;
+import javax.media.j3d.CapabilityNotSetException;
+import javax.media.j3d.Canvas3D;
+
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.SceneGraphObjectState;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.NodeComponentState;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.BranchGroupState;
+import com.sun.j3d.utils.scenegraph.io.UnsupportedUniverseException;
+import com.sun.j3d.utils.universe.SimpleUniverse;
+import com.sun.j3d.utils.universe.ConfiguredUniverse;
+
+public class RandomAccessFileControl extends Controller {
+
+ protected String FILE_IDENT = new String( "j3dff" );
+
+ private long user_data;
+ private long universe_config;
+
+ private long symbol_table;
+
+ private RandomAccessFile raf;
+
+ private int branchGraphCount=0;
+
+ private boolean writeMode = false;
+ private Object userData;
+
+ /** Creates new RandomAccessFileControl */
+ public RandomAccessFileControl() {
+ super();
+ symbolTable = new SymbolTable(this);
+ }
+
+ /**
+ * Create the file and write the inital header information
+ */
+ public void createFile( java.io.File file,
+ SimpleUniverse universe,
+ boolean writeUniverseContent,
+ String description,
+ java.io.Serializable userData ) throws IOException,
+ UnsupportedUniverseException,
+ CapabilityNotSetException {
+
+ raf = new RandomAccessFile( file, "rw" );
+ writeMode = true;
+
+ raf.seek(0);
+ raf.writeUTF( FILE_IDENT );
+
+ raf.seek(20);
+ raf.writeInt( outputFileVersion );
+
+ raf.seek( BRANCH_GRAPH_COUNT );
+ raf.writeInt( 0 ); // Place holder to branch graph count
+
+ raf.seek( FILE_DESCRIPTION );
+
+ if (description==null)
+ description="";
+ raf.writeUTF( description );
+
+ try {
+ writeSerializedData( raf, userData );
+
+ universe_config = raf.getFilePointer();
+ writeUniverse( raf, universe, writeUniverseContent );
+ } catch( SGIORuntimeException e ) {
+ throw new IOException( e.getMessage() );
+ }
+ }
+
+ /**
+ * Open the file for reading
+ */
+ public void openFile( java.io.File file ) throws IOException {
+ raf = new RandomAccessFile( file, "r" );
+ writeMode = false;
+
+ raf.seek(0);
+ String ident = raf.readUTF();
+
+ if ( ident.equals("demo_j3f") )
+ throw new IOException(
+ "Use Java 3D Fly Through I/O instead of Java 3D Scenegraph I/O" );
+
+ if ( !ident.equals("j3dff") )
+ throw new IOException(
+ "This is a Stream - use SceneGraphStreamReader instead");
+
+ raf.seek(20);
+ currentFileVersion = raf.readInt();
+
+ if ( currentFileVersion > outputFileVersion ) {
+ throw new IOException("Unsupported file version. This file was written using a new version of the SceneGraph IO API, please update your installtion to the latest version");
+ }
+
+ // readFileDescription sets user_data
+ String description = readFileDescription();
+
+ raf.seek( BRANCH_GRAPH_COUNT );
+ branchGraphCount = raf.readInt();
+ //System.out.println("BranchGraph count : "+branchGraphCount );
+
+ raf.seek( UNIVERSE_CONFIG_PTR );
+ universe_config = raf.readLong();
+
+ raf.seek( SYMBOL_TABLE_PTR );
+ symbol_table = raf.readLong();
+
+ ConfiguredUniverse universe;
+
+ raf.seek( symbol_table );
+ symbolTable.readTable( raf, false );
+ raf.seek(user_data);
+
+ userData = readSerializedData(raf);
+ }
+
+ public ConfiguredUniverse readUniverse( boolean attachBranchGraphs,
+ Canvas3D canvas) throws IOException {
+ raf.seek( universe_config );
+ return readUniverse( raf, attachBranchGraphs, canvas );
+ }
+
+ public Object getUserData() {
+ return userData;
+ }
+
+ /**
+ * Read the set of branchgraps.
+ *
+ * Used by readUniverse
+ *
+ * RandomAccessFileControl will read the graphs in the array,
+ * StreamControl will read all graphs in the stream
+ */
+ protected void readBranchGraphs( int[] graphs ) throws IOException {
+ for(int i=0; i<graphs.length; i++) {
+ readBranchGraph( graphs[i] );
+ }
+ }
+
+ /**
+ * Return the number of branchgraphs in the file
+ */
+ public int getBranchGraphCount() {
+ return symbolTable.getBranchGraphCount();
+ }
+
+ public void writeBranchGraph( BranchGroup bg, java.io.Serializable userData ) throws IOException {
+ long filePointer = raf.getFilePointer();
+ raf.writeInt( 0 ); // Node count
+ try {
+ writeSerializedData( raf, userData ); // Size and byte[]
+
+ //System.out.println("Actual Write at "+raf.getFilePointer() );
+
+ SymbolTableData symbol = symbolTable.getSymbol( bg );
+
+ if (symbol==null) {
+ symbol = symbolTable.createSymbol( bg );
+ symbol.branchGraphID = -1; // This is a new BranchGraph so set the ID to -1
+ } // which will cause setBranchGraphRoot to assign a new ID.
+
+ symbolTable.setBranchGraphRoot( symbol, filePointer );
+
+ symbolTable.startUnsavedNodeComponentFrame();
+ SceneGraphObjectState state = createState( bg, symbol );
+ //System.out.println(state);
+ try {
+ writeObject( raf, state );
+ writeNodeComponents( raf );
+ } catch( IOException e ) {
+ e.printStackTrace();
+ }
+ symbolTable.endUnsavedNodeComponentFrame();
+ } catch( SGIORuntimeException e ) {
+ throw new IOException( e.getMessage() );
+ }
+
+ }
+
+ public BranchGroup[] readBranchGraph( int graphID ) throws IOException {
+ //System.out.print("Loading graph "+graphID+" : "); // TODO - remove
+ try {
+ int[] dependencies = symbolTable.getBranchGraphDependencies(graphID);
+
+ //System.out.println("Dependencies ");
+ //for(int i=0; i<dependencies.length; i++)
+ // System.out.print( dependencies[i]+" "); // TODO - remove
+ //System.out.println();
+
+ BranchGroupState[] states = new BranchGroupState[ dependencies.length+1 ];
+ BranchGroup[] ret = new BranchGroup[ states.length ];
+ states[0] = readSingleBranchGraph( graphID );
+
+ for( int i=0; i<dependencies.length; i++) {
+ states[i+1] = readSingleBranchGraph( dependencies[i] );
+ }
+
+ for( int i=0; i<states.length; i++) {
+ if (!states[i].getSymbol().graphBuilt) {
+ states[i].buildGraph();
+ states[i].getSymbol().graphBuilt = true;
+ }
+ ret[i] = (BranchGroup)states[i].getNode();
+ }
+
+ symbolTable.clearUnshared(); // Remove all unshared symbols
+
+ return ret;
+ } catch( SGIORuntimeException e ) {
+ throw new IOException( e.getMessage() );
+ }
+ }
+
+ /**
+ * Read and return all the graphs in the file
+ */
+ public BranchGroup[] readAllBranchGraphs() throws IOException {
+ int size = getBranchGraphCount();
+ BranchGroupState[] states = new BranchGroupState[ size ];
+ BranchGroup[] ret = new BranchGroup[ size ];
+
+ try {
+ for( int i=0; i<size; i++) {
+ states[i] = readSingleBranchGraph( i );
+ }
+
+ for( int i=0; i<states.length; i++) {
+ if (!states[i].getSymbol().graphBuilt) {
+ states[i].buildGraph();
+ states[i].getSymbol().graphBuilt = true;
+ }
+ ret[i] = (BranchGroup)states[i].getNode();
+ }
+
+ symbolTable.clearUnshared(); // Remove all unshared symbols
+ } catch( SGIORuntimeException e ) {
+ throw new IOException( e.getMessage() );
+ }
+
+ return ret;
+ }
+
+ /**
+ * Read the specified branchgraph but do NOT call buildGraph
+ */
+ private BranchGroupState readSingleBranchGraph( int graphID ) throws IOException {
+ SymbolTableData symbol = symbolTable.getBranchGraphRoot( graphID );
+
+ if (symbol.nodeState!=null) {
+ return (BranchGroupState)symbol.nodeState;
+ }
+
+ raf.seek( symbolTable.getBranchGraphFilePosition( graphID ) );
+
+ return readNextBranchGraph();
+ }
+
+ /**
+ * Read the next userData and BranchGraph structure in the file
+ * at the current position
+ */
+ private BranchGroupState readNextBranchGraph() throws IOException {
+ int nodeCount = raf.readInt();
+ skipUserData( raf );
+
+ BranchGroupState state=null;
+ try {
+ state = (BranchGroupState)readObject( raf );
+
+ readNodeComponents( raf );
+
+ } catch( IOException e ) {
+ e.printStackTrace();
+ }
+
+ return state;
+ }
+
+ public Object readBranchGraphUserData( int graphID ) throws IOException {
+ try {
+ raf.seek( symbolTable.getBranchGraphFilePosition( graphID ) );
+
+ int nodeCount = raf.readInt();
+ return readSerializedData( raf );
+ } catch( SGIORuntimeException e ) {
+ throw new IOException( e.getMessage() );
+ }
+ }
+
+ /**
+ * Write all the unsaved NodeComponents and SharedGroups to DataOutput.
+ * Mark all the NodeComponents as saved.
+ */
+ protected void writeNodeComponents( DataOutput out ) throws IOException {
+ // The RandomAccessFileControl version sets throws the pointer to
+ // the next NodeComponent correctly
+ long ptrLoc=0L;
+
+ java.util.ListIterator list = symbolTable.getUnsavedNodeComponents();
+ out.writeInt( symbolTable.getUnsavedNodeComponentsSize() );
+ while( list.hasNext() ) {
+ SymbolTableData symbol = (SymbolTableData)list.next();
+
+ out.writeInt( symbol.nodeID );
+ ptrLoc = raf.getFilePointer();
+ out.writeLong( 0L ); // Pointer to next NodeComponent
+
+ writeObject( out, symbol.getNodeState() );
+
+ long ptr = raf.getFilePointer();
+ raf.seek( ptrLoc );
+ out.writeLong( ptr );
+ raf.seek( ptr );
+ }
+ }
+
+ /**
+ * Read in all the node components in this block
+ */
+ protected void readNodeComponents( DataInput in ) throws IOException {
+ int count = in.readInt();
+
+ for(int i=0; i<count; i++) {
+ int nodeID = in.readInt();
+ long nextNC = in.readLong();
+ if (symbolTable.isLoaded( nodeID )) {
+ // Skip this object
+ raf.seek( nextNC );
+ } else {
+ // Reading the objects will register them in the symbol table
+ SceneGraphObjectState nodeComponent = readObject( in );
+ }
+ }
+ }
+
+ //static java.util.LinkedList objSizeTracker = new java.util.LinkedList();
+
+ public void writeObject( DataOutput out, SceneGraphObjectState obj ) throws IOException {
+ symbolTable.setFilePosition( raf.getFilePointer(), obj );
+ try {
+ // These commented out lines will display the size of each object
+ // as it's written to the file
+
+ //long start = raf.getFilePointer();
+
+
+ //int childStart = objSizeTracker.size();
+
+ super.writeObject( out, obj );
+
+ //long size = raf.getFilePointer()-start;
+ //while( childStart!=objSizeTracker.size() )
+ // size -= ((Long)objSizeTracker.removeLast()).longValue();
+
+ //String name = obj.getClass().getName();
+ //System.out.println( name.substring( name.lastIndexOf('.')+1, name.length() )+" size "+size);
+
+ //objSizeTracker.addLast( new Long( size ));
+
+ } catch( SGIORuntimeException e ) {
+ throw new IOException( e.getMessage() );
+ }
+}
+
+ public String readFileDescription() throws IOException {
+ raf.seek( FILE_DESCRIPTION );
+ String ret = raf.readUTF();
+
+ user_data = raf.getFilePointer();
+ return ret;
+ }
+
+ /**
+ * Used by SymbolTable to load a node component that is not in current
+ * graph
+ */
+ public void loadNodeComponent( SymbolTableData symbol ) throws IOException {
+ try {
+ raf.seek( symbol.filePosition );
+ readObject( raf );
+ } catch( SGIORuntimeException e ) {
+ throw new IOException( e.getMessage() );
+ }
+ }
+
+ /**
+ * Loads the specified SharedGroup
+ */
+ public void loadSharedGroup( SymbolTableData symbol ) throws IOException {
+ try {
+ raf.seek( symbol.filePosition );
+ readObject( raf );
+ } catch( SGIORuntimeException e ) {
+ throw new IOException( e.getMessage() );
+ }
+ }
+
+ public void close() throws IOException {
+ try {
+ if (writeMode)
+ writeClose();
+
+ //System.out.println("File size at close "+raf.length() );
+ raf.close();
+ super.reset();
+ } catch( SGIORuntimeException e ) {
+ throw new IOException( e.getMessage() );
+ }
+
+ }
+
+ /**
+ * Write all the pointers etc
+ */
+ private void writeClose() throws IOException {
+ symbol_table = raf.getFilePointer();
+ super.getSymbolTable().writeTable( raf );
+
+ //System.out.println("Symbol table size "+(raf.getFilePointer()-symbol_table));
+
+ raf.seek( UNIVERSE_CONFIG_PTR );
+ raf.writeLong( universe_config );
+ raf.seek( SYMBOL_TABLE_PTR );
+ raf.writeLong( symbol_table );
+ raf.seek( BRANCH_GRAPH_COUNT );
+ raf.writeInt( symbolTable.getBranchGraphCount() );
+ }
+
+ public long getFilePointer() {
+ try {
+ return raf.getFilePointer();
+ } catch(IOException e ) {}
+ return 0;
+ }
+
+ /**
+ * Given a branchgraph, return the corresponding index of the graph
+ * in the file. Returns -1 if graph not found.
+ */
+ public int getBranchGraphPosition( BranchGroup graph ) {
+ SymbolTableData symbol = symbolTable.getSymbol( graph );
+ if ( symbol!=null ) return symbol.branchGraphID;
+ return -1;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/SGIORuntimeException.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/SGIORuntimeException.java
new file mode 100644
index 0000000..560a4d6
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/SGIORuntimeException.java
@@ -0,0 +1,74 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.retained;
+
+/**
+ * This RuntimeException encapsulates a number of RuntimeErrors in the
+ * IO system.
+ *
+ * This exception is always wrapped and thrown to the user as an IOException. In
+ * the future this exception will be removed and the code will throw IOExceptions
+ * internally instead.
+ */
+public class SGIORuntimeException extends java.lang.RuntimeException {
+
+ /**
+ * Constructs a new instance of <code>SGIORuntimeException</code>
+ * without a detail message.
+ */
+ public SGIORuntimeException() {
+ }
+
+
+ /**
+ * Constructs an instance of <code>SGIORuntimeException</code>
+ * with the specified detail message.
+ *
+ * @param msg the detail message.
+ */
+ public SGIORuntimeException(String msg) {
+ super(msg);
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/StreamControl.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/StreamControl.java
new file mode 100644
index 0000000..a61226b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/StreamControl.java
@@ -0,0 +1,205 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.retained;
+
+import java.io.RandomAccessFile;
+import java.io.IOException;
+import java.io.DataOutput;
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import javax.media.j3d.VirtualUniverse;
+import javax.media.j3d.BranchGroup;
+import javax.media.j3d.SceneGraphObject;
+
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.SceneGraphObjectState;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.NodeComponentState;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.BranchGroupState;
+import com.sun.j3d.utils.scenegraph.io.UnsupportedUniverseException;
+
+/**
+ * Provides the infrastructure for ScenGraphStream Reader and Writer
+ */
+public class StreamControl extends Controller {
+
+ protected String FILE_IDENT = new String( "j3dsf" );
+
+ private DataInputStream inputStream;
+ private DataOutputStream outputStream;
+
+ public StreamControl( DataOutputStream out ) {
+ super();
+ outputStream = out;
+ symbolTable = new SymbolTable( this );
+ }
+
+ public StreamControl( DataInputStream in ) {
+ super();
+ inputStream = in;
+ symbolTable = new SymbolTable( this );
+ }
+
+ /**
+ * Prepare the Stream for writing, by sending header information
+ */
+ public void writeStreamHeader() throws IOException {
+ outputStream.writeUTF( FILE_IDENT );
+ outputStream.writeInt( outputFileVersion );
+ }
+
+ public void readStreamHeader() throws IOException {
+ String ident = inputStream.readUTF();
+ if ( ident.equals("demo_j3s") )
+ throw new IOException( "Use Java 3D Fly Through I/O instead of Java 3D Scenegraph I/O" );
+
+ if ( !ident.equals("j3dsf") )
+ throw new IOException(
+ "This is a File - use SceneGraphFileReader instead");
+
+ currentFileVersion = inputStream.readInt();
+
+ if (currentFileVersion > outputFileVersion ) {
+ throw new IOException("Unsupported file version. This file was written using a new version of the SceneGraph IO API, please update your installtion to the latest version");
+ }
+ }
+
+ /**
+ * Add the named objects to the symbol table
+ */
+ public void addNamedObjects( HashMap namedObjects ) {
+ symbolTable.addNamedObjects( namedObjects );
+ }
+
+ /**
+ * The BranchGraph userData is not supported in a stream and will be
+ * ignored.
+ *
+ * However the data in the userData field of the BranchGroup will be
+ * stored in the stream
+ */
+ public void writeBranchGraph( BranchGroup bg, java.io.Serializable userData ) throws IOException {
+ try {
+ SymbolTableData symbol = symbolTable.getSymbol( bg );
+
+ if (symbol==null) {
+ symbol = symbolTable.createSymbol( bg );
+ symbol.branchGraphID = -1; // This is a new BranchGraph so set the ID to -1
+ } // which will cause setBranchGraphRoot to assign a new ID.
+
+ symbolTable.setBranchGraphRoot( symbol, 0 );
+ symbolTable.startUnsavedNodeComponentFrame();
+ SceneGraphObjectState state = createState( bg, symbol );
+ writeObject( outputStream, state );
+ writeNodeComponents( outputStream );
+ symbolTable.endUnsavedNodeComponentFrame();
+
+ if (symbolTable.branchGraphHasDependencies( symbol.branchGraphID ))
+ throw new javax.media.j3d.DanglingReferenceException();
+
+ symbolTable.clearUnshared();
+ symbolTable.writeTable( outputStream );
+ } catch( SGIORuntimeException e ) {
+ throw new IOException( e.getMessage() );
+ }
+ }
+
+ public BranchGroup readBranchGraph( HashMap namedObjects ) throws IOException {
+ try {
+ SceneGraphObjectState state = readObject( inputStream );
+ readNodeComponents( inputStream );
+ symbolTable.readTable( inputStream, true );
+
+ symbolTable.setBranchGraphRoot( state.getSymbol(), 0 );
+
+ state.buildGraph();
+
+ if (namedObjects!=null)
+ symbolTable.getNamedObjectMap( namedObjects );
+
+ return (BranchGroup)state.getNode();
+ } catch( SGIORuntimeException e ) {
+ throw new IOException( e.getMessage() );
+ }
+ }
+
+ /**
+ * Read the set of branchgraps.
+ *
+ * Used by readUniverse
+ *
+ * RandomAccessFileControl will read the graphs in the array,
+ * StreamControl expects the graphs to follow the universe in the
+ * stream so it will read graphs.length branchgraphs.
+ */
+ protected void readBranchGraphs( int[] graphs ) throws IOException {
+ for(int i=0; i<graphs.length; i++)
+ readBranchGraph( null );
+ }
+
+ /**
+ * Used by SymbolTable to load a node component that is not in current
+ * graph
+ */
+ public void loadNodeComponent( SymbolTableData symbol ) throws IOException {
+ throw new java.io.IOException("Unable to load individual NodeComponents from Stream");
+ }
+
+ public void close() throws IOException {
+ super.reset();
+ }
+
+ /**
+ * Implementation of abstract method from Controller.
+ *
+ * Always returns 0
+ */
+ public long getFilePointer() {
+ return 0L;
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/SymbolTable.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/SymbolTable.java
new file mode 100644
index 0000000..5fcf8b3
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/SymbolTable.java
@@ -0,0 +1,851 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.retained;
+
+import java.io.IOException;
+import java.io.DataOutput;
+import java.io.DataInput;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.ListIterator;
+import java.util.LinkedList;
+import java.util.Iterator;
+import java.util.HashSet;
+import java.util.Collection;
+import java.util.Stack;
+
+import javax.media.j3d.SceneGraphObject;
+
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.SceneGraphObjectState;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.NullSceneGraphObjectState;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.NodeComponentState;
+import com.sun.j3d.utils.scenegraph.io.NamedObjectException;
+import com.sun.j3d.utils.scenegraph.io.ObjectNotLoadedException;
+import com.sun.j3d.utils.scenegraph.io.SceneGraphObjectReferenceControl;
+
+/**
+ * SymbolTable class for SceneGraph I/O.
+ */
+public class SymbolTable extends java.lang.Object implements SceneGraphObjectReferenceControl {
+
+ private int nodeID = 1; // ID of zero represents null
+ private HashMap j3dNodeIndex; // Index by SceneGraphObject
+ private ArrayList nodeIDIndex; // Index by NodeID of Nodes
+ private HashMap danglingReferences; // Java3D objects without a current State object
+ private Stack unsavedNodeComponentsStack;
+ private LinkedList sharedNodes; // Nodes and NodeComponents referenced more than once
+ private HashMap namedObjects;
+ private ArrayList branchGraphs; // Root of each branch graph
+ private ArrayList branchGraphDependencies; // Dependencies between the branchgraphs
+ // For a graph branchGraphDep[graph] will contain a set of all nodes (in other graphs) on which the graph is dependent
+
+ private Controller control;
+ private int currentBranchGraphID = -1; // ID's start at 0, -1 is null, -2 is during read, -3 is dangling
+ private int nextBranchGraphID = 0;
+
+ /** Creates new SymbolTable */
+ public SymbolTable( Controller control ) {
+ this.control = control;
+ j3dNodeIndex = new HashMap();
+ danglingReferences = new HashMap();
+ nodeIDIndex = new ArrayList();
+ nodeIDIndex.add( null ); // Element zero is null
+ sharedNodes = new LinkedList();
+ namedObjects = new HashMap();
+ branchGraphs = new ArrayList();
+ branchGraphDependencies = new ArrayList();
+ unsavedNodeComponentsStack = new Stack();
+ }
+
+ /**
+ * At this stage their should be no dangling references
+ *
+ */
+ private void checkforDanglingReferences() {
+ ListIterator list = sharedNodes.listIterator();
+
+ while(list.hasNext()) {
+ SymbolTableData data = (SymbolTableData)list.next();
+ if (data.branchGraphID==-3) {
+ System.err.println("Warning : node "+data.j3dNode+" is referenced but is not attached to a BranchGraph");
+ System.err.println("Setting reference to null. This scene may not look correct when loaded");
+ }
+ }
+ }
+
+ /**
+ * Remove dependencies on objects which are not attached to a
+ * branchgraph
+ */
+ private void removeNullDependencies( HashSet set ) {
+ Iterator it = set.iterator();
+ while( it.hasNext() ) {
+ SymbolTableData symbol = (SymbolTableData)it.next();
+ if (symbol.branchGraphID==-3)
+ it.remove();
+ }
+ }
+
+ public void writeTable( DataOutput out ) throws IOException {
+
+ // At this stage their should be no dangling references
+ checkforDanglingReferences();
+
+ ListIterator list = sharedNodes.listIterator();
+ out.writeInt( sharedNodes.size() );
+ out.writeInt( nodeID );
+ while(list.hasNext()) {
+ SymbolTableData data = (SymbolTableData)list.next();
+ data.writeObject( out );
+ }
+
+ // Write Named objects
+ String[] names = getNames();
+ out.writeInt( names.length );
+ for(int i=0; i<names.length; i++) {
+ out.writeUTF( names[i] );
+ SceneGraphObject node = (SceneGraphObject)namedObjects.get(names[i]);
+ SymbolTableData symbol = getSymbol( node );
+ if (symbol!=null)
+ out.writeInt( symbol.nodeID );
+ else
+ out.writeInt( 0 ); // Null
+ }
+
+ // Write BranchGraph roots
+ out.writeInt( branchGraphs.size() );
+ for(int i=0; i<branchGraphs.size(); i++)
+ ((SymbolTableData)branchGraphs.get(i)).writeObject( out );
+
+ for(int i=0; i<branchGraphDependencies.size(); i++) {
+ HashSet set = (HashSet)branchGraphDependencies.get( i );
+ if (set==null) {
+ out.writeInt( 0 );
+ } else {
+ removeNullDependencies( set );
+ out.writeInt( set.size() );
+ Iterator it = set.iterator();
+ while( it.hasNext() ) {
+ SymbolTableData symbol = (SymbolTableData)it.next();
+ out.writeInt( symbol.nodeID );
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Read and store the entire symbol table
+ *
+ * @param streamRead - true if reading from a Stream in which case only the
+ * branchGraphs and named objects are read.
+ */
+ public void readTable( java.io.DataInput in, boolean streamRead ) throws IOException {
+ int size = in.readInt();
+ nodeID = in.readInt();
+ nodeIDIndexEnsureCapacity( nodeID );
+ for(int i=0; i<size; i++) {
+ SymbolTableData symbol = new SymbolTableData(0,null,null,-1);
+ symbol.readObject( in );
+
+ // If we are loading from a stream then the NodeComponents have
+ // already been loaded and their symbols created. Therefore
+ // the symbols loaded here are discarded.
+ if (!streamRead) {
+ sharedNodes.add( symbol );
+ nodeIDIndex.set( symbol.nodeID, symbol );
+ }
+ }
+
+ // Read Named objects
+ size = in.readInt();
+ for(int j=0; j<size; j++) {
+ String name = in.readUTF();
+ int id = in.readInt();
+ namedObjects.put( name, new Integer(id) );
+ }
+
+ size = in.readInt();
+ //System.out.println("Symbol table BranchGraph size "+size );
+ for(int i=0; i<size; i++)
+ branchGraphs.add( null);
+
+ // Read each branchgraph symbol and check that the symbol is not
+ // already in the symbol table.
+ for(int j=0; j<size; j++) {
+ SymbolTableData tmp = new SymbolTableData(0,null,null,-1);
+ tmp.readObject( in );
+
+ SymbolTableData symbol = getSymbol( tmp.nodeID );
+
+ if (symbol==null) {
+ symbol = tmp;
+ if (symbol.referenceCount>1)
+ sharedNodes.add( symbol );
+ nodeIDIndex.set( symbol.nodeID, symbol );
+ }
+
+ branchGraphs.set( j, symbol );
+ }
+
+
+ for(int i=0; i<size; i++) {
+ int setSize = in.readInt();
+
+ if (setSize==0)
+ branchGraphDependencies.add( null );
+ else {
+ HashSet set = new HashSet();
+ branchGraphDependencies.add( set );
+ for( int j=0; j<setSize; j++) {
+ set.add( getSymbol(in.readInt()));
+ }
+ }
+ }
+ }
+
+ /**
+ * Mark the node referenced by this Symbol as a branch graph root
+ *
+ *The filePointer is the position of the BranchGraph in the file, this
+ *is not the same as the BranchGroups position due to the extra data stored
+ *for a graph.
+ */
+ public void setBranchGraphRoot( SymbolTableData symbol, long filePointer ) {
+ if (symbol.branchGraphID<0 ) {
+ symbol.branchGraphID = nextBranchGraphID++;
+ }
+
+ currentBranchGraphID = symbol.branchGraphID;
+ for(int i=branchGraphs.size(); i<currentBranchGraphID+1; i++) {
+ branchGraphs.add( null);
+ branchGraphDependencies.add( null );
+ }
+
+ branchGraphs.set( currentBranchGraphID, symbol );
+ symbol.branchGraphFilePointer = filePointer;
+ }
+
+ public SymbolTableData getBranchGraphRoot( int graphID ) {
+ //System.out.println("BranchGraph root "+graphID+" "+(SymbolTableData)branchGraphs.get(graphID) );
+ return (SymbolTableData)branchGraphs.get(graphID);
+ }
+
+ /**
+ * Set the branchGraphID in the symbol to the current branch graph ID
+ */
+ public void setBranchGraphID( SymbolTableData symbol ) {
+ symbol.branchGraphID = currentBranchGraphID;
+ }
+
+ /**
+ * Return an array of each BranchGraph on which graphID is dependent for
+ * closure of the graph
+ *
+ * Only Nodes (not node components) cause dependencies
+ *
+ * If there are no dependencies int[0] is returned
+ */
+ public int[] getBranchGraphDependencies( int graphID ) {
+ HashSet set = (HashSet)branchGraphDependencies.get(graphID);
+ if (set==null)
+ return new int[0];
+
+ int[] ret = new int[ set.size() ];
+ Iterator it = set.iterator();
+ int i=0;
+ while( it.hasNext() )
+ ret[i++] = ((SymbolTableData)it.next()).branchGraphID;
+
+ return ret;
+ }
+
+ /**
+ * Return true if the graph is dependent on nodes in
+ * other graphs
+ *
+ * Only Nodes (not node components) cause dependencies
+ *
+ */
+ public boolean branchGraphHasDependencies( int graphID ) {
+ HashSet set = (HashSet)branchGraphDependencies.get(graphID);
+
+ if (set==null || set.size()==0)
+ return false;
+ else
+ return true;
+ }
+
+ public int getBranchGraphCount() {
+ return branchGraphs.size();
+ }
+
+ public long getBranchGraphFilePosition( int graphID ) {
+ SymbolTableData symbol = (SymbolTableData)branchGraphs.get( graphID );
+ return symbol.branchGraphFilePointer;
+ }
+
+
+ /**
+ * Create a new symbol and provide a new nodeID
+ * This is used during the save process
+ */
+ public SymbolTableData createSymbol( SceneGraphObject node ) {
+
+ // TODO : Remove this get, it's here to provide debug consistancy check
+ SymbolTableData data = (SymbolTableData)j3dNodeIndex.get( node );
+
+ SymbolTableData dangling = (SymbolTableData)danglingReferences.get( node );
+
+ //System.out.println("Checking for dangling "+dangling+" "+node);
+
+ if (dangling!=null) {
+ data = dangling;
+ data.branchGraphID = currentBranchGraphID;
+ danglingReferences.remove( dangling );
+ //System.out.println("Updating dangling ref count"); // TODO - remove
+ } else if (data==null) {
+ data = new SymbolTableData( nodeID++, node, null, currentBranchGraphID );
+ j3dNodeIndex.put( node, data );
+ nodeIDIndex.add( data );
+ } else if (data.j3dNode instanceof javax.media.j3d.Node) {
+ throw new RuntimeException( "Object already in Symbol table "+ node );
+ }
+
+ return data;
+ }
+
+
+ /**
+ * Create a new symbol using the specified nodeID
+ * This is used during the load process.
+ */
+ public SymbolTableData createSymbol( SceneGraphObjectState state, SceneGraphObject node,
+ int nodeID ) {
+
+ // TODO : Remove this get, it's here to provide debug consistancy check
+ SymbolTableData data = (SymbolTableData)j3dNodeIndex.get( node );
+
+ if (data==null) {
+ nodeIDIndexEnsureCapacity( nodeID );
+ data = (SymbolTableData)nodeIDIndex.get( nodeID );
+ if (data==null) {
+ data = new SymbolTableData( nodeID, node, state, -2 );
+ j3dNodeIndex.put( node, data );
+ nodeIDIndex.set( data.getNodeID(), data );
+ } else if (data.getJ3dNode()==null) { // Only use state and node if
+ data.j3dNode = node; // this is the first instantiation
+ data.nodeState = state; // of the node
+ j3dNodeIndex.put( node, data );
+ }
+ } else
+ throw new SGIORuntimeException( "Object already in Symbol table ");
+
+ return data;
+ }
+
+ private void nodeIDIndexEnsureCapacity( int size ) {
+ nodeIDIndex.ensureCapacity( size );
+ int adjust = size - nodeIDIndex.size();
+ for(int i=0; i<=adjust; i++)
+ nodeIDIndex.add( null );
+ }
+
+ /**
+ * Create or return the SymbolTableData for a node which does not
+ * necessary have a State object yet
+ *
+ */
+ private SymbolTableData createDanglingSymbol( SceneGraphObject node ) {
+ SymbolTableData data = (SymbolTableData)j3dNodeIndex.get( node );
+
+ if (data==null) {
+ data = new SymbolTableData( nodeID++, node, null, -3 );
+ j3dNodeIndex.put( node, data );
+ nodeIDIndex.add( data );
+ danglingReferences.put( node, data );
+ } else if ( data.nodeState==null) {
+ if (data.referenceCount==1)
+ sharedNodes.add( data );
+ data.referenceCount++;
+ } else
+ throw new SGIORuntimeException( "Object already in Symbol table ");
+
+ return data;
+ }
+
+ private SymbolTableData createNodeComponentSymbol( SceneGraphObject node ) {
+ SymbolTableData symbol = new SymbolTableData( nodeID++, node, null, currentBranchGraphID );
+ symbol.isNodeComponent = true;
+ j3dNodeIndex.put( node, symbol );
+ nodeIDIndex.add( symbol );
+
+ ((LinkedList)unsavedNodeComponentsStack.peek()).add( symbol );
+
+ control.createState( symbol );
+
+ return symbol;
+ }
+
+ public int getUnsavedNodeComponentsSize() {
+ return ((LinkedList)unsavedNodeComponentsStack.peek()).size();
+ }
+
+ public ListIterator getUnsavedNodeComponents() {
+ return ((LinkedList)unsavedNodeComponentsStack.peek()).listIterator(0);
+ }
+
+ public void startUnsavedNodeComponentFrame() {
+ unsavedNodeComponentsStack.push( new LinkedList() );
+ }
+
+ public void endUnsavedNodeComponentFrame() {
+ unsavedNodeComponentsStack.pop();
+ confirmInterGraphDependency();
+ }
+
+ /**
+ * Check for and remove any inter graph dependency
+ * labels that have been resolved to the current graph
+ */
+ private void confirmInterGraphDependency() {
+ HashSet set = (HashSet)branchGraphDependencies.get(currentBranchGraphID);
+ if (set==null)
+ return;
+
+ Iterator it = set.iterator();
+ while(it.hasNext()) {
+ SymbolTableData symbol = (SymbolTableData)it.next();
+ if (symbol.branchGraphID==currentBranchGraphID)
+ it.remove();
+ }
+
+ }
+
+ /**
+ * Add a dependency to the current branchgraph on <code>symbol</code>
+ *
+ * Only nodes (not nodeComponents) affect intergraph dependencies
+ */
+ private void addInterGraphDependency( SymbolTableData symbol ) {
+ HashSet set = (HashSet)branchGraphDependencies.get( currentBranchGraphID );
+ if (set==null) {
+ set = new HashSet();
+ branchGraphDependencies.set( currentBranchGraphID, set );
+ }
+
+ set.add(symbol);
+ }
+
+ /**
+ * Update the reference count for the node component.
+ *
+ * Called during NodeComponentState.addSubReference()
+ */
+ public void incNodeComponentRefCount( int nodeID ) {
+ if (nodeID==0) return;
+
+ SymbolTableData symbol = getSymbol( nodeID );
+
+ ((NodeComponentState)symbol.nodeState).addSubReference();
+
+ if (symbol.referenceCount==1)
+ sharedNodes.add( symbol );
+ symbol.referenceCount++;
+ }
+
+ /**
+ * Add a refernce to the specified node
+ * Also returns the nodes id
+ */
+ public int addReference( SceneGraphObject node ) {
+ if (node==null) return 0;
+
+ SymbolTableData symbol = getSymbol( node );
+
+ if (symbol==null) {
+ if (node instanceof javax.media.j3d.Node) {
+ symbol = createDanglingSymbol( node );
+ if (symbol.branchGraphID != currentBranchGraphID ) {
+ //System.out.println("------------- Adding Reference "+symbol.nodeID+" "+node ); // TODO - remove
+ addInterGraphDependency( symbol );
+ sharedNodes.add( symbol );
+ }
+ } else {
+ symbol = createNodeComponentSymbol( node );
+ }
+ return symbol.nodeID;
+ } else {
+ return addReference( symbol );
+ }
+ }
+
+ /**
+ * Add a refernce to the specified node
+ * Also returns the nodes id
+ */
+ public int addReference( SymbolTableData symbol ) {
+
+ if (symbol!=null) {
+ if (symbol.referenceCount==1)
+ sharedNodes.add( symbol );
+ symbol.referenceCount++;
+
+ if (symbol.j3dNode instanceof javax.media.j3d.NodeComponent && symbol.referenceCount>1) {
+ ((NodeComponentState)symbol.nodeState).addSubReference();
+ }
+
+ if (symbol.branchGraphID != currentBranchGraphID &&
+ symbol.j3dNode instanceof javax.media.j3d.Node ) {
+ // System.out.println("------------- Adding Reference "+symbol.nodeID+" "+symbol.j3dNode ); // TODO - remove
+ addInterGraphDependency( symbol );
+ }
+ } else {
+ throw new SGIORuntimeException("Null Symbol");
+ }
+
+ return symbol.nodeID;
+ }
+
+ /**
+ * Add a refernce to the BranchGraph root
+ * Also returns the nodes id
+ *
+ * Used to associate graphs with a locale without storing the graph at the
+ * current time.
+ */
+ public int addBranchGraphReference( SceneGraphObject node, int branchGraphID ) {
+ if (node==null) return 0;
+
+ SymbolTableData symbol = getSymbol( node );
+
+ if (symbol!=null) {
+ if (symbol.referenceCount==1)
+ sharedNodes.add( symbol );
+ symbol.referenceCount++;
+ } else {
+ symbol = new SymbolTableData( nodeID++, node, null, -3 );
+ j3dNodeIndex.put( node, symbol );
+ nodeIDIndex.add( symbol );
+ danglingReferences.put( node, symbol );
+ }
+
+ symbol.branchGraphID = branchGraphID;
+ for(int i=branchGraphs.size(); i<branchGraphID+1; i++) {
+ branchGraphs.add( null);
+ branchGraphDependencies.add( null );
+ }
+
+ branchGraphs.set( symbol.branchGraphID, symbol );
+
+ return symbol.nodeID;
+ }
+
+ /**
+ * Return true if this node has already been loaded
+ */
+ public boolean isLoaded( int nodeID ) {
+ SymbolTableData symbol = getSymbol( nodeID );
+
+ if (symbol==null)
+ return false;
+
+ if (symbol.j3dNode==null)
+ return false;
+
+ return true;
+ }
+
+ /**
+ * Return the Java3D node associated with the nodeID.
+ *
+ * The method will call buildGraph() on the node if necessary
+ */
+ public SceneGraphObject getJ3dNode( int nodeID ) {
+ if (nodeID==0) return null;
+
+ SymbolTableData symbol = getSymbol( nodeID );
+
+ // Although referenced this node was not attached to the
+ // scenegraph, so return null
+ if (symbol.branchGraphID==-3)
+ return null;
+
+ if (symbol!=null && symbol.j3dNode==null) {
+ if (symbol.isNodeComponent && (control instanceof RandomAccessFileControl) ) {
+ try {
+ ((RandomAccessFileControl)control).loadNodeComponent( symbol );
+ } catch(IOException e ) {
+ System.out.println("FAILED to seek and load NodeComponent");
+ return null;
+ }
+ } else {
+ System.out.println("WARNING - Object has not been loaded "+nodeID);
+ System.out.println("Need to load branchgraph "+symbol.branchGraphID );
+ return null;
+ }
+ } else if (symbol==null) {
+ throw new SGIORuntimeException("Missing Symbol "+nodeID );
+ }
+
+ if ( !symbol.graphBuilt ) {
+ symbol.graphBuilt = true;
+ symbol.nodeState.buildGraph();
+ }
+
+ return symbol.j3dNode;
+ }
+
+ /**
+ * Get the table entry for node
+ */
+ public SymbolTableData getSymbol( SceneGraphObject node ) {
+ //System.out.println("getSymbol "+node+" "+j3dNodeIndex.get( node ));
+ return (SymbolTableData)j3dNodeIndex.get( node );
+ }
+
+ /**
+ * Return the node with the give ID
+ */
+ public SymbolTableData getSymbol( int nodeID ) {
+ // nodeID's start at 1
+
+ if (nodeID==0 || nodeID>nodeIDIndex.size() )
+ return null;
+ else
+ return (SymbolTableData)nodeIDIndex.get( nodeID );
+ }
+
+ /** Get the symbol for the shared group
+ * If the sharedgroup has not been loaded then load it before
+ * returning (if we are using RandomAccessFileControl
+ */
+ public SymbolTableData getSharedGroup( int nodeID ) {
+ SymbolTableData symbol = getSymbol( nodeID );
+
+ if (symbol.nodeState==null && control instanceof RandomAccessFileControl) {
+ try {
+ ((RandomAccessFileControl)control).loadSharedGroup( symbol );
+ } catch( java.io.IOException e ) {
+ e.printStackTrace();
+ throw new SGIORuntimeException("Internal error in getSharedGroup");
+ }
+ }
+
+ return symbol;
+ }
+
+ /**
+ * Set the position of the object referenced by state
+ */
+ public void setFilePosition( long ptr, SceneGraphObjectState state ) {
+ if (state instanceof NullSceneGraphObjectState) return;
+
+ SymbolTableData symbol = getSymbol( state.getNodeID() );
+
+ symbol.filePosition = ptr;
+ }
+ /**
+ * Associate the name with the scene graph object
+ */
+ public void addNamedObject( String name, SceneGraphObject object ) {
+ namedObjects.put( name, object );
+ }
+
+ /**
+ * Add all the named objects in <code>map</code>
+ */
+ public void addNamedObjects( HashMap map ) {
+ namedObjects.putAll( map );
+ }
+
+ /**
+ * Return the SceneGraphObject associated with the name
+ */
+ public SceneGraphObject getNamedObject( String name ) throws NamedObjectException, ObjectNotLoadedException {
+ Object obj = namedObjects.get( name );
+ if (obj==null)
+ throw new NamedObjectException( "Unknown name :"+name );
+
+ if (obj instanceof SceneGraphObject)
+ return (SceneGraphObject)obj;
+ else {
+ SymbolTableData symbol = getSymbol( ((Integer)obj).intValue() );
+ if (symbol==null || symbol.j3dNode==null)
+ throw new ObjectNotLoadedException( ((Integer)obj).toString() );
+ return symbol.j3dNode;
+ }
+ }
+
+ /**
+ * Get all the names of the named objects
+ */
+ public String[] getNames() {
+ return (String[])namedObjects.keySet().toArray( new String[] {} );
+ }
+
+ /**
+ * Add the namedObject mappings to <code>map</code>
+ */
+ public void getNamedObjectMap( HashMap map ) {
+ map.putAll( namedObjects );
+ }
+
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+
+ for(int i=0; i<nodeIDIndex.size(); i++) {
+ SymbolTableData data = (SymbolTableData)nodeIDIndex.get(i);
+ if (data!=null)
+ buf.append( data.nodeID+" "+data.referenceCount+" "+data.filePosition+" "+data.branchGraphID+" "+data.nodeState+"\n" );
+ }
+
+ buf.append("\nShared Objects\n");
+
+ ListIterator l = sharedNodes.listIterator();
+ while(l.hasNext()) {
+ SymbolTableData data = (SymbolTableData)l.next();
+ buf.append( data.nodeID+" "+data.referenceCount+" "+data.filePosition+" "+data.branchGraphID+" "+data.j3dNode+"\n" );
+ }
+
+ buf.append("\nNamed Objects\n");
+
+ String[] names = getNames();
+ for(int i=0; i<names.length; i++)
+ buf.append( names[i]+" "+namedObjects.get(names[i]) );
+
+ buf.append("\nBranch Graphs\n");
+ for(int i=0; i<branchGraphs.size(); i++) {
+ SymbolTableData data = (SymbolTableData)branchGraphs.get(i);
+ if (data==null) System.out.println("Data is null "+i+" "+branchGraphs.size());
+ buf.append( data.nodeID+" "+data.referenceCount+" "+data.filePosition+" "+data.branchGraphID+" "+data.j3dNode+" "+data.nodeState+"\n" );
+ }
+
+ buf.append("\nBranch Graph Dependencies\n");
+ for(int i=0; i<branchGraphDependencies.size(); i++) {
+ buf.append("Graph "+i+" - ");
+ HashSet set = (HashSet)branchGraphDependencies.get(i);
+ if (set!=null) {
+ Iterator it = set.iterator();
+ while(it.hasNext())
+ buf.append( ((SymbolTableData)it.next()).nodeID+" ");
+ }
+ buf.append("\n");
+ }
+
+ buf.append("------------------");
+
+ return buf.toString();
+ }
+
+ /**
+ * Clear all elements from the symbol table
+ */
+ public void clear() {
+ j3dNodeIndex.clear();
+ nodeIDIndex.clear();
+ while (!unsavedNodeComponentsStack.empty())
+ unsavedNodeComponentsStack.pop();
+ danglingReferences.clear();
+ sharedNodes.clear();
+ namedObjects.clear();
+ nodeID = 1;
+ }
+
+ /**
+ * Clear all the Symbols that are not shared with other Graphs in the file
+ *
+ * Remove all Symbols from all structures with referenceCounts=1
+ */
+ public void clearUnshared() {
+ // Convert as many named objects as possible to reference to j3dNode
+ String[] names = getNames();
+ for(int i=0; i<names.length; i++) {
+ try {
+ Object obj = namedObjects.get(names[i]);
+ if (obj instanceof Integer) {
+ SymbolTableData symbol = getSymbol( ((Integer)obj).intValue() );
+ if (symbol!=null && symbol.j3dNode!=null)
+ namedObjects.put( names[i], symbol.j3dNode );
+ }
+ } catch( Exception e ) { e.printStackTrace();}
+ }
+
+ j3dNodeIndex.clear();
+ nodeIDIndex.clear();
+ while (!unsavedNodeComponentsStack.empty())
+ unsavedNodeComponentsStack.pop();
+
+ nodeIDIndexEnsureCapacity( nodeID );
+
+ // Add the shared and dangling Symbols back into the other structures
+ ListIterator list = sharedNodes.listIterator();
+ while(list.hasNext()) {
+ SymbolTableData symbol = (SymbolTableData)list.next();
+ nodeIDIndex.set( symbol.nodeID, symbol );
+ j3dNodeIndex.put( symbol.j3dNode, symbol );
+ }
+
+ Iterator it = danglingReferences.values().iterator();
+ while(it.hasNext()) {
+ SymbolTableData symbol = (SymbolTableData)it.next();
+ nodeIDIndex.set( symbol.nodeID, symbol );
+ j3dNodeIndex.put( symbol.j3dNode, symbol );
+ }
+
+
+ }
+
+ /**
+ * Given a nodeID return the corresponding scene graph object.
+ *
+ * Use only during the load cycle
+ */
+ public javax.media.j3d.SceneGraphObject resolveReference(int nodeID) {
+ return getJ3dNode( nodeID );
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/SymbolTableData.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/SymbolTableData.java
new file mode 100644
index 0000000..6e9ab87
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/retained/SymbolTableData.java
@@ -0,0 +1,134 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.retained;
+
+import java.io.DataOutput;
+import java.io.DataInput;
+import java.io.IOException;
+import javax.media.j3d.SceneGraphObject;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.SceneGraphObjectState;
+
+/**
+ * Encapsulates all the data for a node which is stored in the Symbol table
+ */
+public class SymbolTableData extends java.lang.Object {
+
+ public int nodeID;
+ public SceneGraphObjectState nodeState;
+ public SceneGraphObject j3dNode;
+ public int referenceCount;
+ public long filePosition;
+ public boolean isNodeComponent;
+ public int branchGraphID;
+ public long branchGraphFilePointer;
+ public boolean graphBuilt = false;
+
+ /** Creates new SymbolTableData */
+ public SymbolTableData( int nodeID,
+ SceneGraphObject j3dNode,
+ SceneGraphObjectState nodeState,
+ int branchGraphID ) {
+ this.nodeID = nodeID;
+ this.j3dNode = j3dNode;
+ this.nodeState = nodeState;
+ this.branchGraphID = branchGraphID;
+ this.referenceCount = 1;
+ this.isNodeComponent = false;
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ out.writeInt( nodeID );
+ out.writeInt( referenceCount );
+ out.writeLong( filePosition );
+ out.writeBoolean( isNodeComponent );
+ out.writeInt( branchGraphID );
+ out.writeLong( branchGraphFilePointer );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ nodeID = in.readInt();
+ referenceCount = in.readInt();
+ filePosition = in.readLong();
+ isNodeComponent = in.readBoolean();
+ branchGraphID = in.readInt();
+ branchGraphFilePointer = in.readLong();
+ }
+
+ public final int getNodeID() {
+ return nodeID;
+ }
+
+ public final SceneGraphObjectState getNodeState() {
+ return nodeState;
+ }
+
+ public final void setNodeState( SceneGraphObjectState state ) {
+ nodeState = state;
+ }
+
+ public final SceneGraphObject getJ3dNode() {
+ return j3dNode;
+ }
+
+ public final long getFilePosition() {
+ return filePosition;
+ }
+
+ public final int getReferenceCount() {
+ return referenceCount;
+ }
+
+ public final void incrementReferenceCount() {
+ referenceCount++;
+ }
+
+ public final boolean isNodeComponent() {
+ return isNodeComponent;
+ }
+
+ public String toString() {
+ return new String(nodeID +" "+ filePosition+" "+j3dNode);
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/behaviors/interpolators/KBRotPosScaleSplinePathInterpolatorState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/behaviors/interpolators/KBRotPosScaleSplinePathInterpolatorState.java
new file mode 100644
index 0000000..488b555
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/behaviors/interpolators/KBRotPosScaleSplinePathInterpolatorState.java
@@ -0,0 +1,131 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.behaviors.interpolators;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.TransformGroup;
+import javax.vecmath.Point3f;
+import javax.vecmath.Quat4f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.InterpolatorState;
+import com.sun.j3d.utils.behaviors.interpolators.KBRotPosScaleSplinePathInterpolator;
+import com.sun.j3d.utils.behaviors.interpolators.KBKeyFrame;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.TransformInterpolatorState;
+
+public class KBRotPosScaleSplinePathInterpolatorState extends TransformInterpolatorState {
+
+ private KBKeyFrame[] keyFrames;
+ private Transform3D axisOfTranslation;
+
+ public KBRotPosScaleSplinePathInterpolatorState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+
+ control.writeTransform3D( out, ((KBRotPosScaleSplinePathInterpolator)node).getAxisOfRotPosScale() );
+
+ int length = ((KBRotPosScaleSplinePathInterpolator)node).getArrayLength();
+ out.writeInt( length );
+
+ for(int i=0; i<length; i++) {
+ KBKeyFrame keyFrame = ((KBRotPosScaleSplinePathInterpolator)node).getKeyFrame( i );
+ out.writeFloat( keyFrame.knot );
+ out.writeInt( keyFrame.linear );
+ control.writePoint3f( out, keyFrame.position );
+ out.writeFloat( keyFrame.heading );
+ out.writeFloat( keyFrame.pitch );
+ out.writeFloat( keyFrame.bank );
+ control.writePoint3f( out, keyFrame.scale );
+ out.writeFloat( keyFrame.tension );
+ out.writeFloat( keyFrame.continuity );
+ out.writeFloat( keyFrame.bias );
+ }
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams( in );
+
+ axisOfTranslation = control.readTransform3D( in );
+
+ keyFrames = new KBKeyFrame[in.readInt() ];
+ for(int i=0; i<keyFrames.length; i++) {
+ keyFrames[i] = new KBKeyFrame( in.readFloat(),
+ in.readInt(),
+ control.readPoint3f( in ),
+ in.readFloat(),
+ in.readFloat(),
+ in.readFloat(),
+ control.readPoint3f( in ),
+ in.readFloat(),
+ in.readFloat(),
+ in.readFloat() );
+ }
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] { javax.media.j3d.Alpha.class,
+ TransformGroup.class,
+ Transform3D.class,
+ keyFrames.getClass() },
+ new Object[] { null,
+ null,
+ axisOfTranslation,
+ keyFrames } );
+
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new KBRotPosScaleSplinePathInterpolator( null, null, axisOfTranslation, keyFrames );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/behaviors/interpolators/RotPosScaleTCBSplinePathInterpolatorState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/behaviors/interpolators/RotPosScaleTCBSplinePathInterpolatorState.java
new file mode 100644
index 0000000..93c8a92
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/behaviors/interpolators/RotPosScaleTCBSplinePathInterpolatorState.java
@@ -0,0 +1,128 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.behaviors.interpolators;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.TransformGroup;
+import javax.vecmath.Point3f;
+import javax.vecmath.Quat4f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.InterpolatorState;
+import com.sun.j3d.utils.behaviors.interpolators.TCBSplinePathInterpolator;
+import com.sun.j3d.utils.behaviors.interpolators.RotPosScaleTCBSplinePathInterpolator;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.TransformInterpolatorState;
+import com.sun.j3d.utils.behaviors.interpolators.TCBKeyFrame;
+
+public class RotPosScaleTCBSplinePathInterpolatorState extends TransformInterpolatorState {
+
+ private TCBKeyFrame[] keyFrames;
+ private Transform3D axisOfTranslation;
+
+ public RotPosScaleTCBSplinePathInterpolatorState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+
+ control.writeTransform3D( out, ((RotPosScaleTCBSplinePathInterpolator)node).getAxisOfRotPosScale() );
+
+ int length = ((RotPosScaleTCBSplinePathInterpolator)node).getArrayLength();
+ out.writeInt( length );
+
+ for(int i=0; i<length; i++) {
+ TCBKeyFrame keyFrame = ((RotPosScaleTCBSplinePathInterpolator)node).getKeyFrame( i );
+ out.writeFloat( keyFrame.knot );
+ out.writeInt( keyFrame.linear );
+ control.writePoint3f( out, keyFrame.position );
+ control.writeQuat4f( out, keyFrame.quat );
+ control.writePoint3f( out, keyFrame.scale );
+ out.writeFloat( keyFrame.tension );
+ out.writeFloat( keyFrame.continuity );
+ out.writeFloat( keyFrame.bias );
+ }
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams( in );
+
+ axisOfTranslation = control.readTransform3D( in );
+
+ keyFrames = new TCBKeyFrame[in.readInt() ];
+ for(int i=0; i<keyFrames.length; i++) {
+ keyFrames[i] = new TCBKeyFrame( in.readFloat(),
+ in.readInt(),
+ control.readPoint3f( in ),
+ control.readQuat4f( in ),
+ control.readPoint3f( in ),
+ in.readFloat(),
+ in.readFloat(),
+ in.readFloat() );
+ }
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] { javax.media.j3d.Alpha.class,
+ TransformGroup.class,
+ Transform3D.class,
+ keyFrames.getClass() },
+ new Object[] { null,
+ null,
+ axisOfTranslation,
+ keyFrames } );
+
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new RotPosScaleTCBSplinePathInterpolator( null, null, axisOfTranslation, keyFrames );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/behaviors/mouse/MouseBehaviorState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/behaviors/mouse/MouseBehaviorState.java
new file mode 100644
index 0000000..06d4884
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/behaviors/mouse/MouseBehaviorState.java
@@ -0,0 +1,85 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.behaviors.mouse;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.Interpolator;
+import javax.media.j3d.SceneGraphObject;
+import com.sun.j3d.utils.behaviors.mouse.MouseBehavior;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.BehaviorState;
+import javax.media.j3d.TransformGroup;
+import javax.media.j3d.TransformInterpolator;
+
+public class MouseBehaviorState extends BehaviorState {
+
+ private int target=0;
+
+ public MouseBehaviorState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeInt( control.getSymbolTable().addReference( ((MouseBehavior)node).getTransformGroup() ) );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ target = in.readInt();
+ }
+
+ public void buildGraph() {
+ ((MouseBehavior)node).setTransformGroup(
+ (TransformGroup)control.getSymbolTable().getJ3dNode( target ) );
+
+ super.buildGraph(); // This must be the last call in the method
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/BoxState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/BoxState.java
new file mode 100644
index 0000000..5daeaa6
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/BoxState.java
@@ -0,0 +1,155 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.geometry;
+
+import java.io.*;
+import com.sun.j3d.utils.geometry.Box;
+import javax.media.j3d.Appearance;
+import javax.media.j3d.SceneGraphObject;
+import javax.vecmath.Color3f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class BoxState extends PrimitiveState {
+
+ private float xdim;
+ private float ydim;
+ private float zdim;
+ private int frontAppearance;
+ private int backAppearance;
+ private int topAppearance;
+ private int bottomAppearance;
+ private int leftAppearance;
+ private int rightAppearance;
+
+ public BoxState( SymbolTableData symbol, Controller control ) {
+ super( symbol, control );
+
+ if (node!=null) {
+ frontAppearance = control.getSymbolTable().addReference( ((Box)node).getShape( Box.FRONT ).getAppearance() );
+ backAppearance = control.getSymbolTable().addReference( ((Box)node).getShape( Box.BACK ).getAppearance() );
+ topAppearance = control.getSymbolTable().addReference( ((Box)node).getShape( Box.TOP ).getAppearance() );
+ bottomAppearance = control.getSymbolTable().addReference( ((Box)node).getShape( Box.BOTTOM ).getAppearance() );
+ leftAppearance = control.getSymbolTable().addReference( ((Box)node).getShape( Box.LEFT ).getAppearance() );
+ rightAppearance = control.getSymbolTable().addReference( ((Box)node).getShape( Box.RIGHT ).getAppearance() );
+ }
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeInt( frontAppearance );
+ out.writeInt( backAppearance );
+ out.writeInt( topAppearance );
+ out.writeInt( bottomAppearance );
+ out.writeInt( leftAppearance );
+ out.writeInt( rightAppearance );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject(in);
+
+ frontAppearance = in.readInt();
+ backAppearance = in.readInt();
+ topAppearance = in.readInt();
+ bottomAppearance = in.readInt();
+ leftAppearance = in.readInt();
+ rightAppearance = in.readInt();
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+
+ out.writeFloat( ((Box)node).getXdimension() );
+ out.writeFloat( ((Box)node).getYdimension() );
+ out.writeFloat( ((Box)node).getZdimension() );
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams(in);
+
+ xdim = in.readFloat();
+ ydim = in.readFloat();
+ zdim = in.readFloat();
+ }
+
+ public void buildGraph() {
+ if (frontAppearance == backAppearance &&
+ frontAppearance == topAppearance &&
+ frontAppearance == bottomAppearance &&
+ frontAppearance == leftAppearance &&
+ frontAppearance == rightAppearance ) {
+ ((Box)node).setAppearance( (Appearance)control.getSymbolTable().getJ3dNode( frontAppearance ));
+ } else {
+ ((Box)node).setAppearance( Box.FRONT, (Appearance)control.getSymbolTable().getJ3dNode( frontAppearance ));
+ ((Box)node).setAppearance( Box.BACK, (Appearance)control.getSymbolTable().getJ3dNode( backAppearance ));
+ ((Box)node).setAppearance( Box.TOP, (Appearance)control.getSymbolTable().getJ3dNode( topAppearance ));
+ ((Box)node).setAppearance( Box.BOTTOM, (Appearance)control.getSymbolTable().getJ3dNode( bottomAppearance ));
+ ((Box)node).setAppearance( Box.LEFT, (Appearance)control.getSymbolTable().getJ3dNode( leftAppearance ));
+ ((Box)node).setAppearance( Box.RIGHT, (Appearance)control.getSymbolTable().getJ3dNode( rightAppearance ));
+ }
+
+ super.buildGraph(); // This must be the last call in the method
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ Box box = (Box)super.createNode( j3dClass, new Class[] { Float.TYPE,
+ Float.TYPE,
+ Float.TYPE,
+ Integer.TYPE,
+ Appearance.class },
+ new Object[] { new Float( xdim ),
+ new Float( ydim ),
+ new Float( zdim ),
+ new Integer( primflags ),
+ null } );
+ return box;
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new Box( xdim, ydim, zdim, primflags, null );
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/ColorCubeState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/ColorCubeState.java
new file mode 100644
index 0000000..385553b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/ColorCubeState.java
@@ -0,0 +1,98 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.geometry;
+
+import java.io.*;
+import com.sun.j3d.utils.geometry.ColorCube;
+import javax.media.j3d.Shape3D;
+import javax.media.j3d.SceneGraphObject;
+import javax.vecmath.Color3f;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.Shape3DState;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class ColorCubeState extends Shape3DState {
+
+ private double scale=1.0;
+
+ public ColorCubeState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+
+ out.writeDouble( ((ColorCube)node).getScale() );
+
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams(in);
+
+ scale = in.readDouble();
+ }
+
+ /**
+ * Returns true if the groups children should be saved.
+ *
+ * This is overridden by 'black box' groups such a geometry primitives
+ */
+ protected boolean processChildren() {
+ return false;
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ Shape3D shape = (Shape3D) createNode( j3dClass, new Class[]{ Double.TYPE }, new Object[] { new Double(scale) } );
+
+ return shape;
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new ColorCube( scale );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/ConeState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/ConeState.java
new file mode 100644
index 0000000..3e3b7a8
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/ConeState.java
@@ -0,0 +1,144 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.geometry;
+
+import java.io.*;
+import com.sun.j3d.utils.geometry.Cone;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Appearance;
+import javax.media.j3d.PolygonAttributes;
+import javax.media.j3d.AmbientLight;
+import javax.media.j3d.BoundingSphere;
+import javax.media.j3d.Material;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class ConeState extends PrimitiveState {
+
+ private float radius=1f;
+ private float height=2f;
+ private int xdivision=15;
+ private int ydivision=1;
+ private int bodyAppearance=0;
+ private int capAppearance=0;
+
+ public ConeState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+
+ if (node!=null) {
+ bodyAppearance = control.getSymbolTable().addReference( ((Cone)node).getShape( Cone.BODY ).getAppearance() );
+ capAppearance = control.getSymbolTable().addReference( ((Cone)node).getShape( Cone.CAP ).getAppearance() );
+ }
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ out.writeInt( bodyAppearance );
+ out.writeInt( capAppearance );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject(in);
+ bodyAppearance = in.readInt();
+ capAppearance = in.readInt();
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+
+ out.writeFloat( ((Cone)node).getRadius() );
+ out.writeFloat( ((Cone)node).getHeight() );
+ out.writeInt( ((Cone)node).getXdivisions() );
+ out.writeInt( ((Cone)node).getYdivisions() );
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams(in);
+
+ radius = in.readFloat();
+ height = in.readFloat();
+ xdivision = in.readInt();
+ ydivision = in.readInt();
+ }
+
+ public void buildGraph() {
+
+ if (bodyAppearance == capAppearance ) {
+ ((Cone)node).setAppearance( (Appearance)control.getSymbolTable().getJ3dNode( bodyAppearance ));
+ } else {
+ ((Cone)node).setAppearance( Cone.BODY, (Appearance)control.getSymbolTable().getJ3dNode( bodyAppearance ));
+ ((Cone)node).setAppearance( Cone.CAP, (Appearance)control.getSymbolTable().getJ3dNode( capAppearance ));
+ }
+
+ super.buildGraph(); // This must be the last call in the method
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ // Create the node with a null appearance, we will add the appearance
+ // during build graph
+ Cone cone = (Cone)createNode( j3dClass, new Class[] {
+ Float.TYPE,
+ Float.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ Appearance.class },
+ new Object[] {
+ new Float( radius ),
+ new Float( height ),
+ new Integer( primflags ),
+ new Integer( xdivision ),
+ new Integer( ydivision ),
+ null } );
+
+ return cone;
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new Cone( radius, height, primflags, xdivision, ydivision, null );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/CylinderState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/CylinderState.java
new file mode 100644
index 0000000..3283e31
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/CylinderState.java
@@ -0,0 +1,143 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.geometry;
+
+import java.io.*;
+import com.sun.j3d.utils.geometry.Cylinder;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Appearance;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class CylinderState extends PrimitiveState {
+
+ private float radius=1f;
+ private float height=2f;
+ private int xdivision=15;
+ private int ydivision=1;
+ private int topAppearance;
+ private int bottomAppearance;
+ private int bodyAppearance;
+
+ public CylinderState( SymbolTableData symbol, Controller control ) {
+ super( symbol, control );
+
+ if (node!=null) {
+ bodyAppearance = control.getSymbolTable().addReference( ((Cylinder)node).getShape( Cylinder.BODY ).getAppearance() );
+ topAppearance = control.getSymbolTable().addReference( ((Cylinder)node).getShape( Cylinder.TOP ).getAppearance() );
+ bottomAppearance = control.getSymbolTable().addReference( ((Cylinder)node).getShape( Cylinder.BOTTOM ).getAppearance() );
+ }
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ out.writeInt( topAppearance );
+ out.writeInt( bodyAppearance );
+ out.writeInt( bottomAppearance );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject(in);
+ topAppearance = in.readInt();
+ bodyAppearance = in.readInt();
+ bottomAppearance = in.readInt();
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+
+ out.writeFloat( ((Cylinder)node).getRadius() );
+ out.writeFloat( ((Cylinder)node).getHeight() );
+ out.writeInt( ((Cylinder)node).getXdivisions() );
+ out.writeInt( ((Cylinder)node).getYdivisions() );
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams(in);
+
+ radius = in.readFloat();
+ height = in.readFloat();
+ xdivision = in.readInt();
+ ydivision = in.readInt();
+ }
+
+ public void buildGraph() {
+ if (bodyAppearance == topAppearance && bodyAppearance == bottomAppearance ) {
+ ((Cylinder)node).setAppearance( (Appearance)control.getSymbolTable().getJ3dNode( bodyAppearance ));
+ } else {
+ ((Cylinder)node).setAppearance( Cylinder.BODY, (Appearance)control.getSymbolTable().getJ3dNode( bodyAppearance ));
+ ((Cylinder)node).setAppearance( Cylinder.TOP, (Appearance)control.getSymbolTable().getJ3dNode( topAppearance ));
+ ((Cylinder)node).setAppearance( Cylinder.BOTTOM, (Appearance)control.getSymbolTable().getJ3dNode( bottomAppearance ));
+ }
+ super.buildGraph(); // This must be the last call in the method
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ // Create the node with a null appearance, we will add the appearance
+ // during build graph
+ Cylinder cylinder = (Cylinder)createNode( j3dClass, new Class[] {
+ Float.TYPE,
+ Float.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ Appearance.class },
+ new Object[] {
+ new Float( radius ),
+ new Float( height ),
+ new Integer( primflags ),
+ new Integer( xdivision ),
+ new Integer( ydivision ),
+ null } );
+
+ return cylinder;
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new Cylinder( radius, height, primflags, xdivision, ydivision, null );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/PrimitiveState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/PrimitiveState.java
new file mode 100644
index 0000000..2faee27
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/PrimitiveState.java
@@ -0,0 +1,86 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.geometry;
+
+import java.io.*;
+import com.sun.j3d.utils.geometry.Primitive;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.GroupState;
+
+public class PrimitiveState extends GroupState {
+
+ protected int primflags;
+
+ public PrimitiveState( SymbolTableData symbol, Controller control ) {
+ super( symbol, control );
+
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+
+ out.writeInt( ((Primitive)node).getPrimitiveFlags() );
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams(in);
+
+ primflags = in.readInt();
+ }
+
+ public void buildGraph() {
+ super.buildGraph(); // This must be the last call in the method
+ }
+
+ /**
+ * Returns true if the groups children should be saved.
+ *
+ * This is overridden by 'black box' groups such a geometry primitives
+ */
+ protected boolean processChildren() {
+ return false;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/SphereState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/SphereState.java
new file mode 100644
index 0000000..01152c2
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/SphereState.java
@@ -0,0 +1,120 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.geometry;
+
+import java.io.*;
+import com.sun.j3d.utils.geometry.Sphere;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Appearance;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class SphereState extends PrimitiveState {
+
+ private float radius;
+ private int divisions;
+ private int bodyAppearance;
+
+ public SphereState( SymbolTableData symbol, Controller control ) {
+ super( symbol, control );
+
+ if (node!=null) {
+ bodyAppearance = control.getSymbolTable().addReference( ((Sphere)node).getShape( Sphere.BODY ).getAppearance() );
+ }
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeInt( bodyAppearance );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject(in);
+
+ bodyAppearance = in.readInt();
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+
+ out.writeFloat( ((Sphere)node).getRadius() );
+ out.writeInt( ((Sphere)node).getDivisions() );
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams(in);
+
+ radius = in.readFloat();
+ divisions = in.readInt();
+ }
+
+ public void buildGraph() {
+ ((Sphere)node).setAppearance( (Appearance)control.getSymbolTable().getJ3dNode( bodyAppearance ));
+ super.buildGraph(); // This must be the last call in the method
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ // Create the node with a null appearance, we will add the appearance
+ // during build graph
+ Sphere sphere = (Sphere)createNode( j3dClass, new Class[] {
+ Float.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ javax.media.j3d.Appearance.class },
+ new Object[] {
+ new Float( radius ),
+ new Integer( primflags ),
+ new Integer( divisions ),
+ null } );
+
+ return sphere;
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new Sphere( radius, primflags, divisions, null );
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/Text2DState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/Text2DState.java
new file mode 100644
index 0000000..20e3c0c
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/geometry/Text2DState.java
@@ -0,0 +1,137 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.geometry;
+
+import java.io.*;
+import com.sun.j3d.utils.geometry.Sphere;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Shape3D;
+import javax.vecmath.Color3f;
+import com.sun.j3d.utils.geometry.Text2D;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.LeafState;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class Text2DState extends LeafState {
+
+ // Text2D is really a subclass of Shape3D, however we don't want
+ // the Shape3DState class to save the geometry or appearance data
+ // so this class is a subclass of LeafState and we handle
+ // the CollisionBounds in the read/write Object methods.
+
+ private String text;
+ private Color3f color;
+ private String fontName;
+ private int fontSize;
+ private int fontStyle;
+
+ public Text2DState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+
+ if (node!=null) {
+ Text2D t = (Text2D)node;
+ text = t.getString();
+ color = t.getColor();
+ fontName = t.getFontName();
+ fontSize = t.getFontSize();
+ fontStyle = t.getFontStyle();
+ }
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ control.writeBounds( out, ((Shape3D)node).getCollisionBounds() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject(in);
+ ((Shape3D)node).setCollisionBounds( control.readBounds( in ));
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+
+ out.writeUTF( text );
+ control.writeColor3f( out, color );
+ out.writeUTF( fontName );
+ out.writeInt( fontSize );
+ out.writeInt( fontStyle );
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams(in);
+
+ text = in.readUTF();
+ color = control.readColor3f( in );
+ fontName = in.readUTF();
+ fontSize = in.readInt();
+ fontStyle = in.readInt();
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ // Create the node with a null appearance, we will add the appearance
+ // during build graph
+ Text2D text2D = (Text2D)createNode( j3dClass, new Class[] {
+ String.class,
+ Color3f.class,
+ String.class,
+ Integer.TYPE,
+ Integer.TYPE },
+ new Object[] {
+ text,
+ color,
+ fontName,
+ new Integer( fontSize ),
+ new Integer( fontStyle )
+ } );
+
+ return text2D;
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new Text2D( text, color, fontName, fontSize, fontStyle );
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/image/ImageComponent2DURL.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/image/ImageComponent2DURL.java
new file mode 100644
index 0000000..210a670
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/image/ImageComponent2DURL.java
@@ -0,0 +1,122 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.image;
+
+public class ImageComponent2DURL extends javax.media.j3d.ImageComponent2D {
+
+ private java.net.URL url = null;
+
+ public ImageComponent2DURL( int format, java.awt.image.BufferedImage image ) {
+ super( format, image );
+ }
+
+ public ImageComponent2DURL( int format, java.awt.image.BufferedImage image, java.net.URL url ) {
+ super( format, image );
+ this.url = url;
+ }
+
+ public ImageComponent2DURL( int format, java.awt.image.BufferedImage image,
+ boolean byReference,
+ boolean yUp ) {
+ super( format, image, byReference, yUp );
+ }
+
+ public ImageComponent2DURL( int format, java.awt.image.BufferedImage image,
+ boolean byReference,
+ boolean yUp,
+ java.net.URL url ) {
+ super( format, image, byReference, yUp );
+ this.url = url;
+ }
+
+ public ImageComponent2DURL( int format, int width, int height ) {
+ super( format, width, height );
+ }
+
+ public ImageComponent2DURL( int format, int width, int height,
+ boolean byReference, boolean yUp ) {
+ super( format, width, height, byReference, yUp );
+ }
+
+ public ImageComponent2DURL( int format, java.awt.image.RenderedImage image ) {
+ super( format, image );
+ }
+
+ public ImageComponent2DURL( int format, java.awt.image.RenderedImage image,
+ java.net.URL url ) {
+ super( format, image );
+ this.url = url;
+ }
+
+ public ImageComponent2DURL( int format, java.awt.image.RenderedImage image,
+ boolean byReference, boolean yUp ) {
+ super( format, image, byReference, yUp );
+ }
+
+ public ImageComponent2DURL( int format, java.awt.image.RenderedImage image,
+ boolean byReference, boolean yUp,
+ java.net.URL url ) {
+ super( format, image, byReference, yUp );
+ this.url = url;
+ }
+
+ /**
+ * Set the URL for this image component
+ *
+ * @param url The URL for the image component
+ */
+ public void setURL( java.net.URL url ) {
+ this.url = url;
+ }
+
+ /**
+ * Get the URL for this image component
+ *
+ * @return TheURL for this image component
+ */
+ public java.net.URL getURL() {
+ return url;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/image/ImageComponent2DURLIOListener.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/image/ImageComponent2DURLIOListener.java
new file mode 100644
index 0000000..ca44218
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/image/ImageComponent2DURLIOListener.java
@@ -0,0 +1,71 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.image;
+
+/**
+ * The listener interface which is called when a ImageComponent2DURL is
+ * loaded from the scenegraph file
+ */
+public interface ImageComponent2DURLIOListener {
+
+ /**
+ * The listener method which is called when a ImageComponent2DURL is
+ * loaded from the scenegraph file.
+ *
+ * This method must return a valid ImageComponent2DURL, the returned object
+ * will be placed in the scene graph.
+ *
+ * @param format The image format from ImageComponent
+ * @param width The image width from ImageComponent
+ * @param height The image height from ImageComponent
+ * @param byReference The byReference flag from ImageComponent
+ * @param yUp The yUp flag from ImageComponent
+ * @param url The URL for the image component
+ */
+ public ImageComponent2DURL createImageComponent(
+ int format, int width, int height, boolean byReference, boolean yUp,
+ java.net.URL url );
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/image/ImageComponent2DURLState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/image/ImageComponent2DURLState.java
new file mode 100644
index 0000000..fc2d4b7
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/image/ImageComponent2DURLState.java
@@ -0,0 +1,131 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.image;
+
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.awt.Point;
+import java.awt.image.*;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.ImageComponent2D;
+
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.ImageComponentState;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class ImageComponent2DURLState extends ImageComponentState {
+
+ private static ImageComponent2DURLIOListener listener = new DefaultListener();
+ private java.net.URL url;
+
+ public ImageComponent2DURLState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws
+ IOException {
+ super.writeConstructorParams( out );
+
+ out.writeUTF( ((ImageComponent2DURL)node).getURL().toExternalForm() );
+ }
+
+ public void readConstructorParams( DataInput in ) throws
+ IOException {
+
+ super.readConstructorParams( in );
+
+ String urlString = (String)in.readUTF();
+
+ try {
+ url = new java.net.URL(urlString);
+ } catch( java.net.MalformedURLException e ) {
+ throw new RuntimeException("Bad URL in ImageComponent2DURL "+urlString);
+ }
+ }
+
+ protected SceneGraphObject createNode( Class j3dClass ) {
+ //System.out.println( "createNode 1" );
+ //Thread.dumpStack();
+ return listener.createImageComponent( format, width, height, byReference, yUp, url );
+ }
+
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return listener.createImageComponent( format, width, height, byReference, yUp, url );
+ }
+
+ /**
+ * Set the listener which will handle the creation of this ImageComponent
+ * from it's URL.
+ *
+ * Only a single listener can be set for all ImageComponent2DURL in the
+ * scenegraph.
+ */
+ public static void setLoadListener( ImageComponent2DURLIOListener loadListener ) {
+ listener = loadListener;
+ }
+
+ static class DefaultListener implements ImageComponent2DURLIOListener {
+ public ImageComponent2DURL createImageComponent(
+ int format, int width, int height,
+ boolean byReference, boolean yUp,
+ java.net.URL url ) {
+
+ System.out.println("Default ImageComponent2DURL loader not implemented "+url );
+ //System.out.println();
+
+ return new ImageComponent2DURL( format, width, height );
+ }
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/universe/PlatformGeometryState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/universe/PlatformGeometryState.java
new file mode 100644
index 0000000..1be6c6e
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/universe/PlatformGeometryState.java
@@ -0,0 +1,66 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.universe;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.universe.PlatformGeometry;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.BranchGroupState;
+
+public class PlatformGeometryState extends BranchGroupState {
+
+ public PlatformGeometryState(SymbolTableData symbol,Controller control) {
+ super(symbol, control);
+ }
+
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new PlatformGeometry();
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/universe/SimpleUniverseState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/universe/SimpleUniverseState.java
new file mode 100644
index 0000000..bb31df5
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/universe/SimpleUniverseState.java
@@ -0,0 +1,331 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.universe;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.ArrayList;
+import javax.media.j3d.TransformGroup;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.HiResCoord;
+import javax.media.j3d.Locale;
+import javax.media.j3d.BranchGroup;
+import javax.media.j3d.Canvas3D;
+import javax.vecmath.Matrix4d;
+import com.sun.j3d.utils.universe.MultiTransformGroup;
+import com.sun.j3d.utils.universe.ViewingPlatform;
+import com.sun.j3d.utils.universe.ViewerAvatar;
+import com.sun.j3d.utils.universe.PlatformGeometry;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.SceneGraphObjectState;
+import com.sun.j3d.utils.universe.SimpleUniverse;
+import com.sun.j3d.utils.universe.ConfiguredUniverse;
+
+public class SimpleUniverseState extends java.lang.Object {
+
+ private SimpleUniverse universe=null;
+ private Controller control;
+ private ArrayList localeBGs;
+ private int totalBGs=0;
+ private PlatformGeometryState platformGeom;
+ private ViewerAvatarState viewerAvatar;
+
+ /**
+ * Creates new SimpleUniverseState for writing.
+ */
+ public SimpleUniverseState( ConfiguredUniverse universe, Controller control ) {
+ this.universe = universe;
+ this.control = control;
+ }
+
+ /**
+ * Creates new SimpleUniverseState for writing.
+ */
+ public SimpleUniverseState( SimpleUniverse universe, Controller control ) {
+ this.universe = universe;
+ this.control = control;
+ }
+
+ /**
+ * Creates new SimpleUniverseState for reading.
+ */
+ public SimpleUniverseState( Controller control ) {
+ this.control = control;
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ MultiTransformGroup mtg = universe.getViewingPlatform().getMultiTransformGroup();
+ int mtgSize = mtg.getNumTransforms();
+ out.writeInt( mtgSize );
+
+ // Store the matrix from each MTG transform
+ Transform3D trans = new Transform3D();
+ Matrix4d matrix = new Matrix4d();
+ for(int i=0; i<mtgSize; i++) {
+ TransformGroup tg = mtg.getTransformGroup( i );
+ tg.getTransform( trans );
+ trans.get( matrix );
+ control.writeMatrix4d( out, matrix );
+ }
+
+ control.writeObject( out, control.createState( universe.getViewingPlatform().getPlatformGeometry() ));
+ control.writeObject( out, control.createState( universe.getViewer().getAvatar() ));
+
+ writeLocales( out );
+ }
+
+ public void readObject( DataInput in, Canvas3D canvas ) throws IOException {
+ int mtgSize = in.readInt(); // MultiTransformGroup size
+ if ( canvas!=null) {
+ universe = new ConfiguredUniverse( canvas, mtgSize);
+ } else {
+ universe = new ConfiguredUniverse( ConfiguredUniverse.getConfigURL(), mtgSize);
+ }
+ MultiTransformGroup mtg = universe.getViewingPlatform().getMultiTransformGroup();
+
+ // Read and set the matrix for each MTG transfrom
+ Matrix4d matrix = new Matrix4d();
+ for(int i=0; i<mtgSize; i++) {
+ TransformGroup tg = mtg.getTransformGroup( i );
+ matrix = control.readMatrix4d( in );
+ Transform3D trans = new Transform3D( matrix );
+ tg.setTransform( trans );
+ }
+
+ SceneGraphObjectState tmp = control.readObject(in);
+
+ if (tmp instanceof PlatformGeometryState)
+ platformGeom = (PlatformGeometryState)tmp;
+ else
+ platformGeom = null;
+
+ tmp = control.readObject(in);
+ if (tmp instanceof ViewerAvatarState)
+ viewerAvatar = (ViewerAvatarState)tmp;
+ else
+ viewerAvatar = null;
+
+ readLocales( in );
+ }
+
+ private void writeLocales( DataOutput out ) throws IOException {
+
+ Enumeration allLocales = universe.getAllLocales();
+ out.writeInt( universe.numLocales() );
+ localeBGs = new ArrayList( universe.numLocales() );
+ int currentLocale = 0;
+ int graphID = 0;
+ while( allLocales.hasMoreElements() ) {
+ Locale locale = (Locale)allLocales.nextElement();
+ HiResCoord hiRes = new HiResCoord();
+ writeHiResCoord( out, hiRes );
+ int bgs[];
+ if (currentLocale==0)
+ bgs = new int[locale.numBranchGraphs()-1]; // Disregard ViewingPlatform
+ else
+ bgs = new int[locale.numBranchGraphs()];
+ out.writeInt( bgs.length );
+ int count=0;
+ Enumeration e = locale.getAllBranchGraphs();
+ while( e.hasMoreElements() ) {
+ BranchGroup bg = (BranchGroup)e.nextElement();
+ if (!(bg instanceof ViewingPlatform)) {
+ control.getSymbolTable().addBranchGraphReference( bg, graphID );
+ bgs[count] = graphID++;
+ out.writeInt( bgs[count] );
+ count++;
+ totalBGs++;
+ }
+ }
+ localeBGs.add( bgs );
+ }
+ }
+
+ private void readLocales( DataInput in ) throws IOException {
+ int numLocales = in.readInt();
+ localeBGs = new ArrayList( numLocales );
+ Locale locale;
+ for(int i=0; i<numLocales; i++) {
+ HiResCoord hiRes = readHiResCoord( in );
+
+ if (i==0){ // SimpleUniverse is constructed with a locale so just set its HiRes.
+ locale = universe.getLocale();
+ locale.setHiRes( hiRes );
+ } else {
+ locale = new Locale( universe, hiRes );
+ }
+
+ int numBG = in.readInt();
+ int[] bgs = new int[numBG];
+ for(int n=0; n<numBG; n++) {
+ bgs[i] = in.readInt();
+ totalBGs++;
+ }
+ localeBGs.add( bgs );
+
+ }
+ }
+
+ /**
+ * Called once IO is complete, attaches all the branchgroups to the locales
+ */
+ public void buildGraph() {
+ Locale locale;
+ Enumeration e = universe.getAllLocales();
+ for( int i=0; i<localeBGs.size(); i++) {
+ locale = (Locale)e.nextElement();
+ int[] bgs = (int[])localeBGs.get(i);
+ for(int j=0; j<bgs.length; j++) {
+ SymbolTableData symbol = control.getSymbolTable().getBranchGraphRoot( bgs[j] );
+ locale.addBranchGraph((BranchGroup)symbol.j3dNode );
+ }
+ }
+
+ if (viewerAvatar!=null) {
+ viewerAvatar.buildGraph();
+ universe.getViewer().setAvatar( (ViewerAvatar)viewerAvatar.getNode());
+ }
+
+ if (platformGeom!=null) {
+ universe.getViewingPlatform().setPlatformGeometry( (PlatformGeometry)platformGeom.getNode() );
+ platformGeom.buildGraph();
+ }
+ }
+
+ /**
+ * Return all the branchgraph id's for all Locales in the universe
+ *
+ * This call must be made after readObject()
+ */
+ public int[] getAllGraphIDs() {
+ int[] ret = new int[totalBGs];
+ int c = 0;
+
+ for( int i=0; i<localeBGs.size(); i++) {
+ int[] bgs = (int[])localeBGs.get(i);
+ for(int j=0; j<bgs.length; j++) {
+ ret[ c++ ] = bgs[j];
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Detach each BranchGraph from the Locale(s)
+ */
+ public void detachAllGraphs() {
+ int c = 0;
+
+ try {
+ for( int i=0; i<localeBGs.size(); i++) {
+ int[] bgs = (int[])localeBGs.get(i);
+ for(int j=0; j<bgs.length; j++) {
+ SymbolTableData symbol = control.getSymbolTable().getBranchGraphRoot( bgs[j] );
+ ((BranchGroup)symbol.j3dNode).detach();
+ }
+ }
+ } catch( javax.media.j3d.CapabilityNotSetException e ) {
+ throw new javax.media.j3d.CapabilityNotSetException(
+ "Locale BranchGraphs MUST have ALLOW_DETACH capability set" );
+ }
+ }
+
+ /**
+ * Reattach each BranchGraph to the Locale(s)
+ */
+ public void attachAllGraphs() {
+ Enumeration e = universe.getAllLocales();
+ Locale locale;
+
+ for( int i=0; i<localeBGs.size(); i++) {
+ locale = (Locale)e.nextElement();
+ int[] bgs = (int[])localeBGs.get(i);
+ for(int j=0; j<bgs.length; j++) {
+ SymbolTableData symbol = control.getSymbolTable().getBranchGraphRoot( bgs[j] );
+ locale.addBranchGraph(((BranchGroup)symbol.j3dNode));
+ }
+ }
+ }
+
+ /**
+ * Return the 'node', ie the virtual universe. Returns null if currently writing a
+ * SimpleUniverse.
+ */
+ public ConfiguredUniverse getNode() {
+ if ( universe instanceof ConfiguredUniverse )
+ return (ConfiguredUniverse)universe;
+ else return null;
+ }
+
+ private void writeHiResCoord( DataOutput out, HiResCoord hiRes ) throws IOException {
+ int[] x = new int[8];
+ int[] y = new int[8];
+ int[] z = new int[8];
+ hiRes.getHiResCoord( x,y,z );
+ for(int i=0; i<8; i++) {
+ out.writeInt( x[i] );
+ out.writeInt( y[i] );
+ out.writeInt( z[i] );
+ }
+ }
+
+ private HiResCoord readHiResCoord( DataInput in ) throws IOException {
+ int[] x = new int[8];
+ int[] y = new int[8];
+ int[] z = new int[8];
+ for(int i=0; i<8; i++) {
+ x[i] = in.readInt();
+ y[i] = in.readInt();
+ z[i] = in.readInt();
+ }
+
+ return new HiResCoord( x, y, z );
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/universe/ViewerAvatarState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/universe/ViewerAvatarState.java
new file mode 100644
index 0000000..5340721
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/com/sun/j3d/utils/universe/ViewerAvatarState.java
@@ -0,0 +1,67 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.com.sun.j3d.utils.universe;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.universe.ViewerAvatar;
+
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.BranchGroupState;
+
+public class ViewerAvatarState extends BranchGroupState {
+
+ public ViewerAvatarState(SymbolTableData symbol,Controller control) {
+ super(symbol, control);
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new ViewerAvatar();
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/AlphaState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/AlphaState.java
new file mode 100644
index 0000000..067c177
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/AlphaState.java
@@ -0,0 +1,100 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Alpha;
+
+public class AlphaState extends NodeComponentState {
+
+ public AlphaState(SymbolTableData symbol,Controller control) {
+ super(symbol, control);
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ Alpha alpha = (Alpha)node;
+ out.writeLong( alpha.getAlphaAtOneDuration() );
+ out.writeLong( alpha.getAlphaAtZeroDuration() );
+ out.writeLong( alpha.getDecreasingAlphaDuration() );
+ out.writeLong( alpha.getDecreasingAlphaRampDuration() );
+ out.writeLong( alpha.getIncreasingAlphaDuration() );
+ out.writeLong( alpha.getIncreasingAlphaRampDuration() );
+ out.writeInt( alpha.getLoopCount() );
+ out.writeInt( alpha.getMode() );
+ out.writeLong( alpha.getPhaseDelayDuration() );
+ out.writeLong( alpha.getStartTime() );
+ out.writeLong( alpha.getTriggerTime() );
+ out.writeLong( alpha.getPauseTime() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ Alpha alpha = (Alpha)node;
+ alpha.setAlphaAtOneDuration( in.readLong() );
+ alpha.setAlphaAtZeroDuration( in.readLong() );
+ alpha.setDecreasingAlphaDuration( in.readLong() );
+ alpha.setDecreasingAlphaRampDuration( in.readLong() );
+ alpha.setIncreasingAlphaDuration( in.readLong() );
+ alpha.setIncreasingAlphaRampDuration( in.readLong() );
+ alpha.setLoopCount( in.readInt() );
+ alpha.setMode( in.readInt() );
+ alpha.setPhaseDelayDuration( in.readLong() );
+ alpha.setStartTime( in.readLong() );
+ alpha.setTriggerTime( in.readLong() );
+ long x = in.readLong();
+ if (x!=0L) alpha.pause(x);
+ }
+
+ protected SceneGraphObject createNode() {
+ return new Alpha();
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/AlternateAppearanceState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/AlternateAppearanceState.java
new file mode 100644
index 0000000..55e2273
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/AlternateAppearanceState.java
@@ -0,0 +1,126 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.AlternateAppearance;
+import javax.media.j3d.BoundingLeaf;
+import javax.media.j3d.Appearance;
+import javax.media.j3d.Group;
+
+public class AlternateAppearanceState extends LeafState {
+
+ private int[] scopes;
+ private int appearance;
+ private int influencingBoundingLeaf;
+
+ public AlternateAppearanceState(SymbolTableData symbol,Controller control) {
+ super(symbol, control);
+
+ if (node!=null) {
+ scopes = new int[ ((AlternateAppearance)node).numScopes() ];
+ for(int i=0; i<scopes.length; i++)
+ scopes[i] = control.getSymbolTable().addReference( ((AlternateAppearance)node).getScope(i));
+
+ appearance = control.getSymbolTable().addReference( ((AlternateAppearance)node).getAppearance());
+ influencingBoundingLeaf = control.getSymbolTable().addReference( ((AlternateAppearance)node).getInfluencingBoundingLeaf());
+ }
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeInt( scopes.length );
+ for(int i=0; i<scopes.length; i++)
+ out.writeInt( scopes[i] );
+
+ out.writeInt( appearance );
+ out.writeInt( influencingBoundingLeaf );
+ control.writeBounds( out, ((AlternateAppearance)node).getInfluencingBounds() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ scopes = new int[ in.readInt() ];
+ for(int i=0; i<scopes.length; i++)
+ scopes[i] = in.readInt();
+
+ appearance = in.readInt();
+ influencingBoundingLeaf = in.readInt();
+
+ ((AlternateAppearance)node).setInfluencingBounds( control.readBounds(in));
+ }
+
+ /**
+ * Called when this component reference count is incremented.
+ * Allows this component to update the reference count of any components
+ * that it references.
+ */
+ public void addSubReference() {
+ // Ground and BoundingLeaf not node components
+ control.getSymbolTable().incNodeComponentRefCount( appearance );
+ }
+
+ public void buildGraph() {
+ for(int i=0; i<scopes.length; i++)
+ ((AlternateAppearance)node).addScope( (Group)control.getSymbolTable().getJ3dNode( scopes[i] ));
+
+ ((AlternateAppearance)node).setAppearance( (Appearance)control.getSymbolTable().getJ3dNode( appearance ));
+ ((AlternateAppearance)node).setInfluencingBoundingLeaf( (BoundingLeaf)control.getSymbolTable().getJ3dNode( influencingBoundingLeaf ));
+ super.buildGraph(); // Must be last call in method
+
+ }
+
+ protected SceneGraphObject createNode() {
+ return new AlternateAppearance();
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/AmbientLightState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/AmbientLightState.java
new file mode 100644
index 0000000..d2dbf87
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/AmbientLightState.java
@@ -0,0 +1,61 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.*;
+import javax.media.j3d.AmbientLight;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class AmbientLightState extends LightState {
+
+ public AmbientLightState( SymbolTableData symbol, Controller control ) {
+ super( symbol, control );
+ }
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new AmbientLight();
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/AppearanceState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/AppearanceState.java
new file mode 100644
index 0000000..0a7c087
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/AppearanceState.java
@@ -0,0 +1,191 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.NodeComponent;
+import javax.media.j3d.Appearance;
+import javax.media.j3d.PolygonAttributes;
+import javax.media.j3d.RenderingAttributes;
+import javax.media.j3d.ColoringAttributes;
+import javax.media.j3d.LineAttributes;
+import javax.media.j3d.Material;
+import javax.media.j3d.PointAttributes;
+import javax.media.j3d.TexCoordGeneration;
+import javax.media.j3d.Texture;
+import javax.media.j3d.TextureAttributes;
+import javax.media.j3d.TextureUnitState;
+import javax.media.j3d.TransparencyAttributes;
+
+public class AppearanceState extends NodeComponentState {
+
+ private int polygonAttributes=0;
+ private int renderingAttributes=0;
+ private int coloringAttributes=0;
+ private int lineAttributes=0;
+ private int material=0;
+ private int pointAttributes=0;
+ private int texCoordGeneration=0;
+ private int texture=0;
+ private int textureAttributes=0;
+ private int[] textureUnitState;
+ private int transparencyAttributes=0;
+
+ public AppearanceState( SymbolTableData symbol, Controller control ) {
+ super(symbol, control);
+
+ if (node!=null) { // Node is null during load
+ Appearance app = (Appearance)node;
+ polygonAttributes = control.getSymbolTable().addReference( app.getPolygonAttributes() );
+ renderingAttributes = control.getSymbolTable().addReference( app.getRenderingAttributes() );
+ coloringAttributes = control.getSymbolTable().addReference( app.getColoringAttributes() );
+ lineAttributes = control.getSymbolTable().addReference( app.getLineAttributes() );
+ material = control.getSymbolTable().addReference( app.getMaterial() );
+ pointAttributes = control.getSymbolTable().addReference( app.getPointAttributes() );
+ texCoordGeneration = control.getSymbolTable().addReference( app.getTexCoordGeneration() );
+ texture = control.getSymbolTable().addReference( app.getTexture() );
+ textureAttributes = control.getSymbolTable().addReference( app.getTextureAttributes() );
+
+ TextureUnitState[] texUnitState = app.getTextureUnitState();
+ if (texUnitState!=null) {
+ textureUnitState = new int[ texUnitState.length ];
+ for(int i=0; i<texUnitState.length; i++)
+ textureUnitState[i] = control.getSymbolTable().addReference( texUnitState[i] );
+ } else
+ textureUnitState = new int[ 0 ];
+
+ transparencyAttributes = control.getSymbolTable().addReference( app.getTransparencyAttributes() );
+ }
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeInt( polygonAttributes );
+ out.writeInt( renderingAttributes );
+ out.writeInt( coloringAttributes );
+ out.writeInt( lineAttributes );
+ out.writeInt( material );
+ out.writeInt( pointAttributes );
+ out.writeInt( texCoordGeneration );
+ out.writeInt( texture );
+ out.writeInt( textureAttributes );
+ out.writeInt( textureUnitState.length );
+ for(int i=0; i<textureUnitState.length; i++)
+ out.writeInt( textureUnitState[i] );
+ out.writeInt( transparencyAttributes );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ polygonAttributes = in.readInt();
+ renderingAttributes = in.readInt();
+ coloringAttributes = in.readInt();
+ lineAttributes = in.readInt();
+ material = in.readInt();
+ pointAttributes = in.readInt();
+ texCoordGeneration = in.readInt();
+ texture = in.readInt();
+ textureAttributes = in.readInt();
+ textureUnitState = new int[in.readInt()];
+ for( int i=0; i<textureUnitState.length; i++)
+ textureUnitState[i] = in.readInt();
+ transparencyAttributes = in.readInt();
+ }
+
+ /**
+ * Called when this component reference count is incremented.
+ * Allows this component to update the reference count of any components
+ * that it references.
+ */
+ public void addSubReference() {
+ control.getSymbolTable().incNodeComponentRefCount( polygonAttributes );
+ control.getSymbolTable().incNodeComponentRefCount( renderingAttributes );
+ control.getSymbolTable().incNodeComponentRefCount( coloringAttributes );
+ control.getSymbolTable().incNodeComponentRefCount( lineAttributes );
+ control.getSymbolTable().incNodeComponentRefCount( material );
+ control.getSymbolTable().incNodeComponentRefCount( pointAttributes );
+ control.getSymbolTable().incNodeComponentRefCount( texCoordGeneration );
+ control.getSymbolTable().incNodeComponentRefCount( textureAttributes );
+ control.getSymbolTable().incNodeComponentRefCount( texture );
+ for(int i=0; i<textureUnitState.length; i++)
+ control.getSymbolTable().incNodeComponentRefCount( textureUnitState[i] );
+ control.getSymbolTable().incNodeComponentRefCount( transparencyAttributes );
+ }
+
+ public void buildGraph() {
+ Appearance app = (Appearance)node;
+ app.setPolygonAttributes( (PolygonAttributes)control.getSymbolTable().getJ3dNode(polygonAttributes) );
+ app.setRenderingAttributes( (RenderingAttributes)control.getSymbolTable().getJ3dNode(renderingAttributes) );
+ app.setColoringAttributes( (ColoringAttributes)control.getSymbolTable().getJ3dNode(coloringAttributes) );
+ app.setLineAttributes( (LineAttributes)control.getSymbolTable().getJ3dNode(lineAttributes) );
+ app.setMaterial( (Material)control.getSymbolTable().getJ3dNode(material) );
+ app.setPointAttributes( (PointAttributes)control.getSymbolTable().getJ3dNode(pointAttributes) );
+ app.setTexCoordGeneration( (TexCoordGeneration)control.getSymbolTable().getJ3dNode(texCoordGeneration) );
+ app.setTextureAttributes( (TextureAttributes)control.getSymbolTable().getJ3dNode(textureAttributes) );
+ app.setTexture( (Texture)control.getSymbolTable().getJ3dNode(texture) );
+
+ TextureUnitState[] texUnitState = new TextureUnitState[ textureUnitState.length ];
+ for(int i=0; i<textureUnitState.length; i++)
+ texUnitState[i] = (TextureUnitState)control.getSymbolTable().getJ3dNode(textureUnitState[i]);
+
+ if (texUnitState.length>0) // TODO - remove if, workaround for bug in daily
+ app.setTextureUnitState( texUnitState );
+
+ app.setTransparencyAttributes( (TransparencyAttributes)control.getSymbolTable().getJ3dNode(transparencyAttributes) );
+
+ super.buildGraph(); // Must be last call in method
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new Appearance();
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/AuralAttributesState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/AuralAttributesState.java
new file mode 100644
index 0000000..ff7fcb0
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/AuralAttributesState.java
@@ -0,0 +1,125 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.AuralAttributes;
+import javax.media.j3d.SceneGraphObject;
+import javax.vecmath.Vector3f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class AuralAttributesState extends NodeComponentState {
+
+ public AuralAttributesState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeFloat( ((AuralAttributes)node).getAttributeGain() );
+
+ float[] distance = new float[ ((AuralAttributes)node).getDistanceFilterLength() ];
+ float[] cutoff = new float[ distance.length ];
+
+ ((AuralAttributes)node).getDistanceFilter( distance, cutoff );
+ out.writeInt( distance.length );
+ for(int i=0; i<distance.length; i++) {
+ out.writeFloat( distance[i] );
+ out.writeFloat( cutoff[i] );
+ }
+
+ out.writeFloat( ((AuralAttributes)node).getFrequencyScaleFactor() );
+ out.writeFloat( ((AuralAttributes)node).getReflectionCoefficient() );
+ control.writeBounds( out, ((AuralAttributes)node).getReverbBounds() );
+ out.writeFloat( ((AuralAttributes)node).getReverbDelay() );
+ out.writeInt( ((AuralAttributes)node).getReverbOrder() );
+ out.writeFloat( ((AuralAttributes)node).getRolloff() );
+ out.writeFloat( ((AuralAttributes)node).getVelocityScaleFactor() );
+ out.writeFloat( ((AuralAttributes)node).getReflectionDelay() );
+ out.writeFloat( ((AuralAttributes)node).getReverbCoefficient() );
+ out.writeFloat( ((AuralAttributes)node).getDecayTime() );
+ out.writeFloat( ((AuralAttributes)node).getDecayFilter() );
+ out.writeFloat( ((AuralAttributes)node).getDiffusion() );
+ out.writeFloat( ((AuralAttributes)node).getDensity() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ ((AuralAttributes)node).setAttributeGain( in.readFloat() );
+
+ float[] distance = new float[ in.readInt() ];
+ float[] cutoff = new float[ distance.length ];
+ for(int i=0; i<distance.length; i++) {
+ distance[i] = in.readFloat();
+ cutoff[i] = in.readFloat();
+ }
+ ((AuralAttributes)node).setDistanceFilter( distance, cutoff );
+
+ ((AuralAttributes)node).setFrequencyScaleFactor( in.readFloat() );
+ ((AuralAttributes)node).setReflectionCoefficient( in.readFloat() );
+ ((AuralAttributes)node).setReverbBounds( control.readBounds(in) );
+ ((AuralAttributes)node).setReverbDelay( in.readFloat() );
+ ((AuralAttributes)node).setReverbOrder( in.readInt() );
+ ((AuralAttributes)node).setRolloff( in.readFloat() );
+ ((AuralAttributes)node).setVelocityScaleFactor( in.readFloat() );
+ ((AuralAttributes)node).setReflectionDelay( in.readFloat() );
+ ((AuralAttributes)node).setReverbCoefficient( in.readFloat() );
+ ((AuralAttributes)node).setDecayTime( in.readFloat() );
+ ((AuralAttributes)node).setDecayFilter( in.readFloat() );
+ ((AuralAttributes)node).setDiffusion( in.readFloat() );
+ ((AuralAttributes)node).setDensity( in.readFloat() );
+ }
+
+ protected SceneGraphObject createNode() {
+ return new AuralAttributes();
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BackgroundSoundState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BackgroundSoundState.java
new file mode 100644
index 0000000..277a8d9
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BackgroundSoundState.java
@@ -0,0 +1,74 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.BackgroundSound;
+import javax.media.j3d.SceneGraphObject;
+import javax.vecmath.Point3f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class BackgroundSoundState extends SoundState {
+
+ public BackgroundSoundState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ }
+
+ protected SceneGraphObject createNode() {
+ return new BackgroundSound();
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BackgroundState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BackgroundState.java
new file mode 100644
index 0000000..ad9df98
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BackgroundState.java
@@ -0,0 +1,125 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Background;
+import javax.media.j3d.BoundingLeaf;
+import javax.media.j3d.Appearance;
+import javax.media.j3d.ImageComponent2D;
+import javax.media.j3d.BranchGroup;
+import javax.vecmath.Color3f;
+
+public class BackgroundState extends LeafState {
+
+ private int image;
+ private int boundingLeaf;
+ private int geometry;
+
+ public BackgroundState(SymbolTableData symbol,Controller control) {
+ super(symbol, control);
+
+ if (node!=null) {
+ boundingLeaf = control.getSymbolTable().addReference( ((Background)node).getApplicationBoundingLeaf());
+ geometry = control.getSymbolTable().addReference( ((Background)node).getGeometry());
+ image = control.getSymbolTable().addReference( ((Background)node).getImage());
+ }
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeInt( boundingLeaf );
+ out.writeInt( geometry );
+ out.writeInt( image );
+ control.writeBounds( out, ((Background)node).getApplicationBounds() );
+ Color3f clr = new Color3f();
+ ((Background)node).getColor(clr);
+ control.writeColor3f( out, clr );
+ out.writeInt( ((Background)node).getImageScaleMode() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ boundingLeaf = in.readInt();
+ geometry = in.readInt();
+ image = in.readInt();
+
+ ((Background)node).setApplicationBounds( control.readBounds(in));
+ ((Background)node).setColor( control.readColor3f(in));
+
+ ((Background)node).setImageScaleMode( in.readInt() );
+ }
+
+ /**
+ * Called when this component reference count is incremented.
+ * Allows this component to update the reference count of any components
+ * that it references.
+ */
+ public void addSubReference() {
+ // geometry and boundingLeaf not node components
+ control.getSymbolTable().incNodeComponentRefCount( image );
+ }
+
+ public void buildGraph() {
+ ((Background)node).setApplicationBoundingLeaf( (BoundingLeaf)control.getSymbolTable().getJ3dNode( boundingLeaf ));
+ ((Background)node).setGeometry( (BranchGroup)control.getSymbolTable().getJ3dNode( geometry ));
+ ((Background)node).setImage( (ImageComponent2D)control.getSymbolTable().getJ3dNode( image ));
+ super.buildGraph(); // Must be last call in method
+
+ }
+
+ protected SceneGraphObject createNode() {
+ return new Background();
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BehaviorState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BehaviorState.java
new file mode 100644
index 0000000..df62679
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BehaviorState.java
@@ -0,0 +1,106 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.Behavior;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.BoundingLeaf;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class BehaviorState extends LeafState {
+
+ private int boundingLeaf;
+
+ public BehaviorState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+
+ }
+
+ protected SceneGraphObject createNode( String className ) {
+ SceneGraphObject ret;
+ try {
+ ret = super.createNode( className );
+ } catch( com.sun.j3d.utils.scenegraph.io.retained.SGIORuntimeException e ) {
+ ret = new com.sun.j3d.utils.scenegraph.io.UnresolvedBehavior();
+ }
+
+ return ret;
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ Behavior beh = (Behavior)node;
+
+ out.writeBoolean( beh.getEnable() );
+ out.writeInt( control.getSymbolTable().addReference( beh.getSchedulingBoundingLeaf() ) );
+
+ control.writeBounds( out, beh.getSchedulingBounds() );
+
+ // We had a lot of dicussion about this - may want to expand support
+ // in future versions, but for now just save and restore scheduling
+ // interval
+ out.writeInt( beh.getSchedulingInterval() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ Behavior beh = (Behavior)node;
+
+ beh.setEnable( in.readBoolean() );
+ boundingLeaf = in.readInt();
+ beh.setSchedulingBounds( control.readBounds( in ) );
+ beh.setSchedulingInterval( in.readInt() );
+ }
+
+ public void buildGraph() {
+ ((Behavior)node).setSchedulingBoundingLeaf( (BoundingLeaf)control.getSymbolTable().getJ3dNode( boundingLeaf ));
+ super.buildGraph(); // Must be last call in method
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BillboardState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BillboardState.java
new file mode 100644
index 0000000..7fba3f0
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BillboardState.java
@@ -0,0 +1,105 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.Billboard;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.TransformGroup;
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class BillboardState extends BehaviorState {
+
+ private int target;
+
+ public BillboardState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+
+ if (node!=null)
+ target = control.getSymbolTable().addReference( ((Billboard)node).getTarget() );
+
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeInt( ((Billboard)node).getAlignmentMode() );
+
+ Vector3f vec = new Vector3f();
+ ((Billboard)node).getAlignmentAxis( vec );
+
+ Point3f point = new Point3f();
+ ((Billboard)node).getRotationPoint( point );
+
+ control.writeVector3f( out, vec );
+ control.writePoint3f( out, point );
+
+ out.writeInt( target );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ ((Billboard)node).setAlignmentMode( in.readInt() );
+ ((Billboard)node).setAlignmentAxis( control.readVector3f( in ) );
+ ((Billboard)node).setRotationPoint( control.readPoint3f( in ) );
+
+ target = in.readInt();
+ }
+
+ public void buildGraph() {
+ ((Billboard)node).setTarget( (TransformGroup)control.getSymbolTable().getJ3dNode( target ));
+ super.buildGraph(); // Must be last call in method
+ }
+
+ protected SceneGraphObject createNode() {
+ return new Billboard();
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BoundingLeafState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BoundingLeafState.java
new file mode 100644
index 0000000..01d45a4
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BoundingLeafState.java
@@ -0,0 +1,87 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.*;
+import javax.media.j3d.BoundingLeaf;
+import javax.media.j3d.Bounds;
+import javax.media.j3d.BoundingBox;
+import javax.media.j3d.BoundingSphere;
+import javax.media.j3d.BoundingPolytope;
+import javax.media.j3d.NodeComponent;
+import javax.media.j3d.SceneGraphObject;
+import javax.vecmath.Point3d;
+import javax.vecmath.Vector4d;
+
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public abstract class BoundingLeafState extends SceneGraphObjectState {
+
+ public BoundingLeafState(SymbolTableData symbol,Controller control) {
+ super(symbol, control);
+ }
+
+ public void writeObject( DataOutput out ) throws
+ IOException {
+
+ super.writeObject( out );
+
+ control.writeBounds( out, ((BoundingLeaf)node).getRegion() );
+ }
+
+ public void readObject( DataInput in ) throws
+ IOException {
+ super.readObject(in);
+
+ ((BoundingLeaf)node).setRegion( control.readBounds(in) );
+ }
+
+ protected SceneGraphObject createNode() {
+ return new BoundingLeaf();
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BranchGroupState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BranchGroupState.java
new file mode 100644
index 0000000..768ce6e
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/BranchGroupState.java
@@ -0,0 +1,63 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import javax.media.j3d.BranchGroup;
+import javax.media.j3d.SceneGraphObject;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class BranchGroupState extends GroupState {
+
+ /** Creates new BranchGroupState */
+ public BranchGroupState( SymbolTableData symbol, Controller control ) {
+ super( symbol, control );
+ }
+
+ protected SceneGraphObject createNode() {
+ return new BranchGroup();
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ClipState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ClipState.java
new file mode 100644
index 0000000..fd2c257
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ClipState.java
@@ -0,0 +1,86 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.Clip;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.BoundingLeaf;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public abstract class ClipState extends LeafState {
+
+ private int boundingLeaf;
+
+ public ClipState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+
+ if (node!=null)
+ boundingLeaf = control.getSymbolTable().addReference( ((Clip)node).getApplicationBoundingLeaf() );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ out.writeInt( boundingLeaf );
+ control.writeBounds( out, ((Clip)node).getApplicationBounds() );
+ out.writeDouble( ((Clip)node).getBackDistance() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ boundingLeaf = in.readInt();
+ ((Clip)node).setApplicationBounds( control.readBounds( in ));
+ ((Clip)node).setBackDistance( in.readDouble() );
+ }
+
+ public void buildGraph() {
+ ((Clip)node).setApplicationBoundingLeaf( (BoundingLeaf)control.getSymbolTable().getJ3dNode( boundingLeaf ));
+ super.buildGraph(); // Must be last call in method
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ColorInterpolatorState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ColorInterpolatorState.java
new file mode 100644
index 0000000..4ef6afa
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ColorInterpolatorState.java
@@ -0,0 +1,114 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.ColorInterpolator;
+import javax.media.j3d.Material;
+import javax.media.j3d.SceneGraphObject;
+import javax.vecmath.Color3f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class ColorInterpolatorState extends InterpolatorState {
+
+ private int target;
+
+ public ColorInterpolatorState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+
+ if (node!=null)
+ target = control.getSymbolTable().addReference( ((ColorInterpolator)node).getTarget() );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeInt( target );
+ Color3f clr = new Color3f();
+ ((ColorInterpolator)node).getStartColor( clr );
+ control.writeColor3f( out, clr );
+ ((ColorInterpolator)node).getEndColor( clr );
+ control.writeColor3f( out, clr );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ target = in.readInt();
+ ((ColorInterpolator)node).setStartColor( control.readColor3f( in ) );
+ ((ColorInterpolator)node).setEndColor( control.readColor3f( in ) );
+ }
+
+ /**
+ * Called when this component reference count is incremented.
+ * Allows this component to update the reference count of any components
+ * that it references.
+ */
+ public void addSubReference() {
+ control.getSymbolTable().incNodeComponentRefCount( target );
+ }
+
+ public void buildGraph() {
+ ((ColorInterpolator)node).setTarget( (Material)control.getSymbolTable().getJ3dNode( target ));
+ super.buildGraph(); // Must be last call in method
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] { javax.media.j3d.Alpha.class,
+ javax.media.j3d.Material.class },
+ new Object[] { null,
+ null } );
+
+ }
+
+ protected SceneGraphObject createNode() {
+ return new ColorInterpolator( null, null );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ColoringAttributesState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ColoringAttributesState.java
new file mode 100644
index 0000000..cdd9548
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ColoringAttributesState.java
@@ -0,0 +1,84 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.ColoringAttributes;
+import javax.vecmath.Color3f;
+
+public class ColoringAttributesState extends NodeComponentState {
+
+ public ColoringAttributesState( SymbolTableData symbol, Controller control ) {
+ super(symbol, control);
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ ColoringAttributes attr = (ColoringAttributes)node;
+ Color3f clr = new Color3f();
+ attr.getColor( clr );
+ control.writeColor3f( out, clr );
+ out.writeInt( attr.getShadeModel() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ ColoringAttributes attr = (ColoringAttributes)node;
+ attr.setColor( control.readColor3f(in) );
+ attr.setShadeModel( in.readInt() );
+ }
+
+ protected SceneGraphObject createNode() {
+ return new ColoringAttributes();
+ }
+
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/CompressedGeometryState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/CompressedGeometryState.java
new file mode 100644
index 0000000..22c4be5
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/CompressedGeometryState.java
@@ -0,0 +1,151 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.vecmath.Point3d;
+import javax.media.j3d.CompressedGeometry;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.CompressedGeometryHeader;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class CompressedGeometryState extends GeometryState {
+
+ private byte[] bytes;
+ private boolean isByReference;
+ private CompressedGeometryHeader header;
+
+ public CompressedGeometryState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+
+ out.writeBoolean( ((CompressedGeometry)node).isByReference() );
+
+ int size = ((CompressedGeometry)node).getByteCount();
+ out.writeInt( size );
+ bytes = new byte[ size ];
+ ((CompressedGeometry)node).getCompressedGeometry( bytes );
+ out.write( bytes );
+
+ header = new CompressedGeometryHeader();
+ ((CompressedGeometry)node).getCompressedGeometryHeader( header );
+ writeCompressedGeometryHeader( out );
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams( in );
+
+ isByReference = in.readBoolean();
+ bytes = new byte[ in.readInt() ];
+ in.readFully( bytes );
+
+ header = new CompressedGeometryHeader();
+ readCompressedGeometryHeader( in );
+ }
+
+ private void writeCompressedGeometryHeader( DataOutput out ) throws IOException {
+ out.writeInt( header.majorVersionNumber );
+ out.writeInt( header.minorVersionNumber );
+ out.writeInt( header.minorMinorVersionNumber );
+ out.writeInt( header.bufferType );
+ out.writeInt( header.bufferDataPresent );
+ out.writeInt( header.size );
+ out.writeInt( header.start );
+ if (header.lowerBound == null) {
+ control.writePoint3d(out, new Point3d(-1, -1, -1));
+ } else {
+ control.writePoint3d( out, header.lowerBound );
+ }
+ if (header.upperBound == null) {
+ control.writePoint3d(out, new Point3d(1, 1, 1));
+ } else {
+ control.writePoint3d( out, header.upperBound );
+ }
+ }
+
+ private void readCompressedGeometryHeader( DataInput in ) throws IOException {
+ header.majorVersionNumber = in.readInt();
+ header.minorVersionNumber = in.readInt();
+ header.minorMinorVersionNumber = in.readInt();
+ header.bufferType = in.readInt();
+ header.bufferDataPresent = in.readInt();
+ header.size = in.readInt();
+ header.start = in.readInt();
+ header.lowerBound = control.readPoint3d( in );
+ if ((header.lowerBound.x == -1) &&
+ (header.lowerBound.y == -1) &&
+ (header.lowerBound.z == -1)) {
+ header.lowerBound = null;
+ }
+ header.upperBound = control.readPoint3d( in );
+ if ((header.upperBound.x == 1) &&
+ (header.upperBound.y == 1) &&
+ (header.upperBound.z == 1)) {
+ header.upperBound = null;
+ }
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+
+ return createNode( j3dClass, new Class[] { CompressedGeometryHeader.class,
+ bytes.getClass(),
+ Boolean.TYPE },
+ new Object[] { header,
+ bytes,
+ new Boolean(isByReference) } );
+ }
+
+ protected SceneGraphObject createNode() {
+ return new CompressedGeometry( header, bytes, isByReference );
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ConeSoundState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ConeSoundState.java
new file mode 100644
index 0000000..737f7bb
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ConeSoundState.java
@@ -0,0 +1,122 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.ConeSound;
+import javax.media.j3d.SceneGraphObject;
+import javax.vecmath.Vector3f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class ConeSoundState extends PointSoundState {
+
+ public ConeSoundState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ float[] distanceAtten = new float[ ((ConeSound)node).getAngularAttenuationLength() ];
+ float[] gainAtten = new float[ distanceAtten.length ];
+ float[] filterAtten = new float[ distanceAtten.length ];
+ float[] backDistance = new float[ distanceAtten.length ];
+ float[] backGain = new float[ distanceAtten.length ];
+ float[] frontDistance = new float[ distanceAtten.length ];
+ float[] frontGain = new float[ distanceAtten.length ];
+
+ ((ConeSound)node).getDistanceGain( frontDistance, frontGain, backDistance, backGain );
+ ((ConeSound)node).getAngularAttenuation( distanceAtten, gainAtten, filterAtten );
+ out.writeInt( distanceAtten.length );
+ for(int i=0; i<distanceAtten.length; i++) {
+ out.writeFloat( distanceAtten[i] );
+ out.writeFloat( gainAtten[i] );
+ out.writeFloat( filterAtten[i] );
+ out.writeFloat( backDistance[i] );
+ out.writeFloat( backGain[i] );
+
+ // We don't need to write the front distance or gain as these
+ // will be handled by the superclass
+ }
+
+ Vector3f direction = new Vector3f();
+
+ ((ConeSound)node).getDirection( direction );
+ control.writeVector3f( out, direction );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ float[] distanceAtten = new float[ in.readInt() ];
+ float[] gainAtten = new float[ distanceAtten.length ];
+ float[] filterAtten = new float[ distanceAtten.length ];
+ float[] backDistance = new float[ distanceAtten.length ];
+ float[] backGain = new float[ distanceAtten.length ];
+
+ for(int i=0; i<distanceAtten.length; i++) {
+ distanceAtten[i] = in.readFloat();
+ gainAtten[i] = in.readFloat();
+ filterAtten[i] = in.readFloat();
+ backDistance[i] = in.readFloat();
+ backGain[i] = in.readFloat();
+ }
+
+ ((ConeSound)node).setBackDistanceGain( backDistance, backGain );
+ ((ConeSound)node).setAngularAttenuation( distanceAtten, gainAtten, filterAtten );
+
+ ((ConeSound)node).setDirection( control.readVector3f( in ));
+ }
+
+ protected SceneGraphObject createNode() {
+ return new ConeSound();
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DecalGroupState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DecalGroupState.java
new file mode 100644
index 0000000..b8cd44f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DecalGroupState.java
@@ -0,0 +1,64 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import javax.media.j3d.DecalGroup;
+import javax.media.j3d.SceneGraphObject;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class DecalGroupState extends GroupState {
+
+ /** Creates new BranchGroupState */
+ public DecalGroupState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ protected SceneGraphObject createNode() {
+ return new DecalGroup();
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DepthComponentFloatState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DepthComponentFloatState.java
new file mode 100644
index 0000000..c0cd148
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DepthComponentFloatState.java
@@ -0,0 +1,111 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.DepthComponentFloat;
+
+public class DepthComponentFloatState extends NodeComponentState {
+
+ private int height;
+ private int width;
+
+ public DepthComponentFloatState ( SymbolTableData symbol, Controller control ) {
+ super(symbol, control);
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ float[] buf = new float[ width*height ];
+ ((DepthComponentFloat)node).getDepthData( buf );
+
+ for ( int i=0; i<buf.length; i++ ) {
+ out.writeFloat( buf[ i ] );
+ }
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ float[] buf = new float[ width*height ];
+
+ for ( int i=0; i<buf.length; i++ ) {
+ buf[ i ] = in.readFloat();
+ }
+ ((DepthComponentFloat)node).setDepthData( buf );
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+ out.writeInt( width );
+ out.writeInt( height );
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams( in );
+
+ width = in.readInt();
+ height = in.readInt();
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] { Integer.TYPE,
+ Integer.TYPE } ,
+ new Object[] { new Integer( width ),
+ new Integer( height ) } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new DepthComponentFloat( width, height );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DepthComponentIntState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DepthComponentIntState.java
new file mode 100644
index 0000000..9a66945
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DepthComponentIntState.java
@@ -0,0 +1,111 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.DepthComponentInt;
+
+public class DepthComponentIntState extends NodeComponentState {
+
+ private int height;
+ private int width;
+
+ public DepthComponentIntState ( SymbolTableData symbol, Controller control ) {
+ super(symbol, control);
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ int[] buf = new int[ width*height ];
+ ((DepthComponentInt)node).getDepthData( buf );
+
+ for ( int i=0; i<buf.length; i++ ) {
+ out.writeInt( buf[ i ] );
+ }
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ int[] buf = new int[ width*height ];
+
+ for ( int i=0; i<buf.length; i++ ) {
+ buf[ i ] = in.readInt();
+ }
+ ((DepthComponentInt)node).setDepthData( buf );
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+ out.writeInt( width );
+ out.writeInt( height );
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams( in );
+
+ width = in.readInt();
+ height = in.readInt();
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] { Integer.TYPE,
+ Integer.TYPE } ,
+ new Object[] { new Integer( width ),
+ new Integer( height ) } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new DepthComponentInt( width, height );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DepthComponentNativeState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DepthComponentNativeState.java
new file mode 100644
index 0000000..50c5641
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DepthComponentNativeState.java
@@ -0,0 +1,94 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.DepthComponentNative;
+
+/**
+ * We have no access to the data inside a DepthComponentNative and
+ * it doesn't really make sense anyway, so we're going to save the
+ * width and height and not save/restore any of the data.
+ */
+public class DepthComponentNativeState extends NodeComponentState {
+
+ private int height;
+ private int width;
+
+ public DepthComponentNativeState ( SymbolTableData symbol, Controller control ) {
+ super(symbol, control);
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+ out.writeInt( width );
+ out.writeInt( height );
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams( in );
+
+ width = in.readInt();
+ height = in.readInt();
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] { Integer.TYPE,
+ Integer.TYPE } ,
+ new Object[] { new Integer( width ),
+ new Integer( height ) } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new DepthComponentNative( width, height );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DirectionalLightState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DirectionalLightState.java
new file mode 100644
index 0000000..462bf0b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DirectionalLightState.java
@@ -0,0 +1,77 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.*;
+import javax.media.j3d.DirectionalLight;
+import javax.vecmath.Vector3f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class DirectionalLightState extends LightState {
+
+ public DirectionalLightState( SymbolTableData symbol, Controller control ) {
+ super( symbol, control );
+
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ Vector3f dir = new Vector3f();
+ ((DirectionalLight)node).getDirection( dir );
+
+ control.writeVector3f( out, dir );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ ((DirectionalLight)node).setDirection( control.readVector3f(in) );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new DirectionalLight();
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DistanceLODState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DistanceLODState.java
new file mode 100644
index 0000000..610ea8d
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/DistanceLODState.java
@@ -0,0 +1,108 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.DistanceLOD;
+import javax.vecmath.Point3f;
+
+public class DistanceLODState extends LODState {
+
+ private int numDistances;
+
+ public DistanceLODState(SymbolTableData symbol,Controller control) {
+ super(symbol, control);
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ for(int i=0; i<numDistances; i++)
+ out.writeDouble( ((DistanceLOD)node).getDistance(i) );
+
+ Point3f pos = new Point3f();
+ ((DistanceLOD)node).getPosition( pos );
+ control.writePoint3f( out, pos );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ for(int i=0; i<numDistances; i++)
+ ((DistanceLOD)node).setDistance( i, in.readDouble() );
+
+ ((DistanceLOD)node).setPosition( control.readPoint3f(in ));
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+ numDistances = ((DistanceLOD)node).numDistances();
+ out.writeInt( numDistances );
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams( in );
+ numDistances = in.readInt();
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ float[] distances = new float[ numDistances ];
+
+ return createNode( j3dClass, new Class[] { distances.getClass() },
+ new Object[] { distances } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new DistanceLOD( new float[ numDistances ] );
+ }
+
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ExponentialFogState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ExponentialFogState.java
new file mode 100644
index 0000000..bd341b7
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ExponentialFogState.java
@@ -0,0 +1,78 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.ExponentialFog;
+import javax.media.j3d.SceneGraphObject;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class ExponentialFogState extends FogState {
+
+ public ExponentialFogState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeFloat( ((ExponentialFog)node).getDensity() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ ((ExponentialFog)node).setDensity( in.readFloat() );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new ExponentialFog();
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/FogState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/FogState.java
new file mode 100644
index 0000000..7a9221a
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/FogState.java
@@ -0,0 +1,108 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.*;
+import javax.media.j3d.Fog;
+import javax.media.j3d.BoundingLeaf;
+import javax.media.j3d.Group;
+import javax.media.j3d.NodeComponent;
+import javax.media.j3d.SceneGraphObject;
+import javax.vecmath.Color3f;
+
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public abstract class FogState extends SceneGraphObjectState {
+
+ protected int[] scopes;
+ protected int boundingLeaf;
+
+ public FogState(SymbolTableData symbol,Controller control) {
+ super(symbol, control);
+ }
+
+ public void writeObject( DataOutput out ) throws
+ IOException {
+
+ super.writeObject( out );
+
+ control.writeBounds( out, ((Fog)node).getInfluencingBounds() );
+
+ out.writeInt( ((Fog)node).numScopes() );
+ for(int i=0; i<((Fog)node).numScopes(); i++)
+ out.writeInt( control.getSymbolTable().addReference( ((Fog)node).getScope(i) ) );
+
+ out.writeInt( control.getSymbolTable().addReference( ((Fog)node).getInfluencingBoundingLeaf() ));
+
+ Color3f clr = new Color3f();
+ ((Fog)node).getColor( clr );
+
+ control.writeColor3f( out, clr );
+ }
+
+ public void readObject( DataInput in ) throws
+ IOException {
+ super.readObject(in);
+
+ ((Fog)node).setInfluencingBounds( control.readBounds(in) );
+ scopes = new int[ in.readInt() ];
+ for(int i=0; i<scopes.length; i++)
+ scopes[i] = in.readInt();
+
+ boundingLeaf = in.readInt();
+ ((Fog)node).setColor( control.readColor3f(in) );
+ }
+
+ public void buildGraph() {
+ for(int i=0; i<scopes.length; i++)
+ ((Fog)node).addScope( (Group)control.getSymbolTable().getJ3dNode( scopes[i] ) );
+
+ ((Fog)node).setInfluencingBoundingLeaf( (BoundingLeaf)control.getSymbolTable().getJ3dNode( boundingLeaf ) );
+ super.buildGraph(); // Must be last call in method
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/Font3DState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/Font3DState.java
new file mode 100644
index 0000000..2f07435
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/Font3DState.java
@@ -0,0 +1,163 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import javax.media.j3d.Font3D;
+import javax.media.j3d.FontExtrusion;
+import javax.media.j3d.SceneGraphObject;
+import java.awt.Font;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import java.awt.Shape;
+import java.lang.String;
+import java.lang.Integer;
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.awt.geom.PathIterator;
+import java.awt.geom.GeneralPath;
+
+public class Font3DState extends NodeComponentState {
+
+ private Font font=null;
+ private double tesselationTolerance=0.0D;
+ private FontExtrusion extrudePath=null;
+
+ public Font3DState( SymbolTableData symbol, Controller control ) {
+ super( symbol, control );
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+
+ out.writeUTF( font.getFontName() );
+ out.writeInt( font.getStyle() );
+ out.writeInt( font.getSize() );
+
+ out.writeDouble( tesselationTolerance );
+
+ if ( extrudePath!=null ) {
+ Shape shape = extrudePath.getExtrusionShape();
+ if ( shape != null ) {
+ PathIterator shapePath = shape.getPathIterator( null );
+ float[] coords = new float[ 6 ];
+ int segType;
+ int points;
+ while ( !(shapePath.isDone()) ) {
+ // Get type of current path segment and associated coordinates
+ segType = shapePath.currentSegment( coords );
+ out.writeInt( segType );
+
+ // Write out relevant coordinates
+ points = 0;
+ if ( segType==PathIterator.SEG_MOVETO) points = 1;
+ else if ( segType==PathIterator.SEG_LINETO ) points = 1;
+ else if (segType==PathIterator.SEG_QUADTO ) points = 2;
+ else if (segType==PathIterator.SEG_CUBICTO ) points = 3;
+
+ for (int i=0;i<points;i++ ) {
+ out.writeFloat( coords[ i*2+0 ] );
+ out.writeFloat( coords[ i*2+1 ] );
+ }
+
+ // Next segment
+ if ( !(shapePath.isDone()) ) shapePath.next();
+ }
+ }
+ // Flag for end of path definition
+ out.writeInt( Integer.MIN_VALUE );
+ out.writeDouble( extrudePath.getTessellationTolerance() );
+ } else out.writeInt( Integer.MIN_VALUE );
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams( in );
+
+ String fontName = in.readUTF();
+ int style = in.readInt();
+ int size = in.readInt();
+ font = new Font( fontName, style, size );
+
+ tesselationTolerance = in.readDouble();
+
+ GeneralPath shape = null;
+ int segType = in.readInt();
+ while ( segType!=Integer.MIN_VALUE ) {
+ if ( shape==null ) shape = new GeneralPath();
+
+ if ( segType==PathIterator.SEG_MOVETO) {
+ shape.moveTo( in.readFloat(), in.readFloat() );
+ } else if ( segType==PathIterator.SEG_LINETO ) {
+ shape.lineTo( in.readFloat(), in.readFloat() );
+ } else if ( segType==PathIterator.SEG_QUADTO ) {
+ shape.quadTo( in.readFloat(), in.readFloat(),
+ in.readFloat(), in.readFloat() );
+ } else if ( segType==PathIterator.SEG_CUBICTO ) {
+ shape.curveTo( in.readFloat(), in.readFloat(),
+ in.readFloat(), in.readFloat(),
+ in.readFloat(), in.readFloat() );
+ } else if ( segType==PathIterator.SEG_CLOSE ) {
+ shape.closePath();
+ }
+
+ segType = in.readInt();
+ }
+ if ( shape!=null ) extrudePath = new FontExtrusion( shape, in.readDouble() );
+ else extrudePath = null;
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] { Font.class,
+ Double.TYPE,
+ FontExtrusion.class },
+ new Object[] { font,
+ new Double( tesselationTolerance ),
+ extrudePath } );
+ }
+
+ protected SceneGraphObject createNode() {
+ return new Font3D( font, tesselationTolerance, extrudePath );
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/GeometryArrayState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/GeometryArrayState.java
new file mode 100644
index 0000000..3c002ae
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/GeometryArrayState.java
@@ -0,0 +1,925 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.*;
+import javax.media.j3d.GeometryArray;
+import javax.media.j3d.IndexedGeometryArray;
+import javax.media.j3d.GeometryStripArray;
+import javax.media.j3d.SceneGraphObject;
+import javax.vecmath.*;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.J3DBuffer;
+import com.sun.j3d.internal.ByteBufferWrapper;
+import com.sun.j3d.internal.BufferWrapper;
+import com.sun.j3d.internal.FloatBufferWrapper;
+import com.sun.j3d.internal.DoubleBufferWrapper;
+import com.sun.j3d.internal.ByteOrderWrapper;
+
+public abstract class GeometryArrayState extends GeometryState {
+
+ protected int vertexFormat;
+ protected int vertexCount;
+ protected int texCoordSetCount;
+ protected int[] texCoordSetMap;
+
+ private static final int FORMAT_NULL = 0;
+ private static final int FORMAT_BYTE = 1;
+ private static final int FORMAT_FLOAT = 2;
+ private static final int FORMAT_DOUBLE = 3;
+ private static final int FORMAT_3B = 4;
+ private static final int FORMAT_4B = 5;
+ private static final int FORMAT_2F = 6;
+ private static final int FORMAT_3F = 7;
+ private static final int FORMAT_4F = 8;
+ private static final int FORMAT_2D = 9;
+ private static final int FORMAT_3D = 10;
+
+ public GeometryArrayState( SymbolTableData symbol, Controller control ) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ boolean nio = (vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0;
+
+ if ( (vertexFormat & GeometryArray.INTERLEAVED)!=0 ) {
+
+ if ( !(node instanceof IndexedGeometryArray) ) {
+ out.writeInt( ((GeometryArray)node).getInitialVertexIndex() );
+ if ( !(node instanceof GeometryStripArray) )
+ out.writeInt( ((GeometryArray)node).getValidVertexCount() );
+ }
+
+ if ( nio ) {
+ FloatBufferWrapper x = new FloatBufferWrapper(
+ ((GeometryArray)node).getInterleavedVertexBuffer());
+ float[] f = new float[x.limit()];
+ x.position( 0 );
+ x.get( f );
+ writeFloatArray( out, f );
+ } else writeFloatArray( out, ((GeometryArray)node).getInterleavedVertices() );
+ } else {
+ boolean byRef = (vertexFormat & GeometryArray.BY_REFERENCE) != 0;
+
+ if ( !(node instanceof IndexedGeometryArray) ) {
+ if ( !byRef )
+ out.writeInt( ((GeometryArray)node).getInitialVertexIndex() );
+ if ( !(node instanceof GeometryStripArray) )
+ out.writeInt( ((GeometryArray)node).getValidVertexCount() );
+ }
+
+ if ( (vertexFormat & GeometryArray.COLOR_4)==GeometryArray.COLOR_4 ) {
+ //System.out.println("COLOR 4");
+ if ( byRef ) {
+ if ( !(node instanceof IndexedGeometryArray) ) {
+ out.writeInt( ((GeometryArray)node).getInitialColorIndex() );
+ }
+
+ if ( nio ) {
+ J3DBuffer buf = ((GeometryArray)node).getColorRefBuffer();
+ switch( BufferWrapper.getBufferType( buf ) ) {
+ case BufferWrapper.TYPE_BYTE: {
+ out.writeInt( FORMAT_BYTE );
+ ByteBufferWrapper bb = new ByteBufferWrapper( buf );
+ byte[] bytes = new byte[ bb.limit() ];
+ bb.position( 0 );
+ bb.get( bytes );
+ out.writeInt( bytes.length );
+ out.write( bytes );
+ }
+ break;
+ case BufferWrapper.TYPE_FLOAT: {
+ out.writeInt( FORMAT_FLOAT );
+ FloatBufferWrapper bb = new FloatBufferWrapper( buf );
+ float[] floats = new float[ bb.limit() ];
+ bb.position( 0 );
+ bb.get( floats );
+ writeFloatArray( out, floats );
+ }
+ break;
+ case BufferWrapper.TYPE_NULL: {
+ out.writeInt( FORMAT_NULL );
+ }
+ break;
+ }
+ } else if ( ((GeometryArray)node).getColorRef4f()!=null ) {
+ out.writeInt( FORMAT_4F );
+ Color4f[] colors = ((GeometryArray)node).getColorRef4f();
+ float[] data = new float[ colors.length*4 ];
+ for (int i = 0 ; i < colors.length ; i++) {
+ data[ i*4+0 ] = colors[i].x;
+ data[ i*4+1 ] = colors[i].y;
+ data[ i*4+2 ] = colors[i].z;
+ data[ i*4+3 ] = colors[i].w;
+ }
+ writeFloatArray( out, data );
+ } else if ( ((GeometryArray)node).getColorRefFloat()!=null ) {
+ out.writeInt( FORMAT_FLOAT );
+ writeFloatArray( out, ((GeometryArray)node).getColorRefFloat() );
+ } else if ( ((GeometryArray)node).getColorRefByte()!=null ) {
+ out.writeInt( FORMAT_BYTE );
+ byte[] colors = ((GeometryArray)node).getColorRefByte();
+ out.writeInt( colors.length );
+ out.write( colors );
+ } else if ( ((GeometryArray)node).getColorRef4b()!=null ) {
+ out.writeInt( FORMAT_4B );
+ Color4b[] colors = ((GeometryArray)node).getColorRef4b();
+ out.writeInt( colors.length );
+ byte[] data = new byte[ colors.length*4 ];
+ for (int i = 0 ; i < colors.length ; i++) {
+ data[ i*4+0 ] = colors[i].x;
+ data[ i*4+1 ] = colors[i].y;
+ data[ i*4+2 ] = colors[i].z;
+ data[ i*4+3 ] = colors[i].w;
+ }
+ out.write( data );
+ } else out.writeInt( FORMAT_NULL );
+ } else {
+ byte[] colors = new byte[ vertexCount*4 ];
+ ((GeometryArray)node).getColors( 0, colors );
+ out.write( colors );
+ }
+ } else if ((vertexFormat & GeometryArray.COLOR_3)==GeometryArray.COLOR_3 ) {
+ //System.out.println("COLOR 3");
+ if ( byRef ) {
+ if ( !(node instanceof IndexedGeometryArray) ) {
+ out.writeInt( ((GeometryArray)node).getInitialColorIndex() );
+ }
+
+ if ( nio ) {
+ J3DBuffer buf = ((GeometryArray)node).getColorRefBuffer();
+ switch( BufferWrapper.getBufferType( buf ) ) {
+ case BufferWrapper.TYPE_BYTE: {
+ out.writeInt( FORMAT_BYTE );
+ ByteBufferWrapper bb = new ByteBufferWrapper( buf );
+ byte[] bytes = new byte[ bb.limit() ];
+ bb.position( 0 );
+ bb.get( bytes );
+ out.writeInt( bytes.length );
+ out.write( bytes );
+ }
+ break;
+ case BufferWrapper.TYPE_FLOAT: {
+ out.writeInt( FORMAT_FLOAT );
+ FloatBufferWrapper bb = new FloatBufferWrapper( buf );
+ float[] floats = new float[ bb.limit() ];
+ bb.position( 0 );
+ bb.get( floats );
+ writeFloatArray( out, floats );
+ }
+ break;
+ case BufferWrapper.TYPE_NULL: {
+ out.writeInt( FORMAT_NULL );
+ }
+ break;
+ }
+ } else if ( ((GeometryArray)node).getColorRef3f()!=null ) {
+ out.writeInt( FORMAT_3F );
+ Color3f[] colors = ((GeometryArray)node).getColorRef3f();
+ float[] data = new float[ colors.length*3 ];
+ for (int i = 0 ; i < colors.length ; i++) {
+ data[ i*3+0 ] = colors[i].x;
+ data[ i*3+1 ] = colors[i].y;
+ data[ i*3+2 ] = colors[i].z;
+ }
+ writeFloatArray( out, data );
+ } else if ( ((GeometryArray)node).getColorRefFloat()!=null ) {
+ out.writeInt( FORMAT_FLOAT );
+ writeFloatArray( out, ((GeometryArray)node).getColorRefFloat() );
+ } else if ( ((GeometryArray)node).getColorRefByte()!=null ) {
+ out.writeInt( FORMAT_BYTE );
+ byte[] colors = ((GeometryArray)node).getColorRefByte();
+ out.writeInt( colors.length );
+ out.write( colors );
+ } else if ( ((GeometryArray)node).getColorRef3b()!=null ) {
+ out.writeInt( FORMAT_3B );
+ Color3b[] colors = ((GeometryArray)node).getColorRef3b();
+ out.writeInt( colors.length );
+ byte[] data = new byte[ colors.length*3 ];
+ for (int i = 0 ; i < colors.length ; i++) {
+ data[ i*3+0 ] = colors[i].x;
+ data[ i*3+1 ] = colors[i].y;
+ data[ i*3+2 ] = colors[i].z;
+ }
+ out.write( data );
+ } else out.writeInt( FORMAT_NULL );
+ } else {
+ byte[] colors3 = new byte[ vertexCount*3 ];
+ ((GeometryArray)node).getColors( 0, colors3 );
+ out.write( colors3 );
+ }
+ }
+
+
+ if ((vertexFormat & GeometryArray.COORDINATES)!=0 ) {
+ //System.out.println("COORDS");
+ if ( byRef ) {
+ if ( !(node instanceof IndexedGeometryArray) ) {
+ out.writeInt( ((GeometryArray)node).getInitialCoordIndex() );
+ }
+
+ if ( nio ) {
+ J3DBuffer buf = ((GeometryArray)node).getCoordRefBuffer();
+ switch( BufferWrapper.getBufferType( buf ) ) {
+ case BufferWrapper.TYPE_FLOAT: {
+ out.writeInt( FORMAT_FLOAT );
+ FloatBufferWrapper bb = new FloatBufferWrapper( buf );
+ float[] floats = new float[ bb.limit() ];
+ bb.position( 0 );
+ bb.get( floats );
+ writeFloatArray( out, floats );
+ }
+ break;
+ case BufferWrapper.TYPE_DOUBLE: {
+ out.writeInt( FORMAT_DOUBLE );
+ DoubleBufferWrapper bb = new DoubleBufferWrapper( buf );
+ double[] doubles = new double[ bb.limit() ];
+ bb.position( 0 );
+ bb.get( doubles );
+ writeDoubleArray( out, doubles );
+ }
+ break;
+ case BufferWrapper.TYPE_NULL: {
+ out.writeInt( FORMAT_NULL );
+ }
+ break;
+ }
+ } else if ( ((GeometryArray)node).getCoordRef3f()!=null ) {
+ out.writeInt( FORMAT_3F );
+ Point3f[] coords = ((GeometryArray)node).getCoordRef3f();
+ float[] data = new float[ coords.length*3 ];
+ for (int i = 0 ; i < coords.length ; i++) {
+ data[ i*3+0 ] = coords[i].x;
+ data[ i*3+1 ] = coords[i].y;
+ data[ i*3+2 ] = coords[i].z;
+ }
+ writeFloatArray( out, data );
+ } else if ( ((GeometryArray)node).getCoordRef3d()!=null ) {
+ out.writeInt( FORMAT_3D );
+ Point3d[] coords = ((GeometryArray)node).getCoordRef3d();
+ double[] data = new double[ coords.length*3 ];
+ for (int i = 0 ; i < coords.length ; i++) {
+ data[ i*3+0 ] = coords[i].x;
+ data[ i*3+1 ] = coords[i].y;
+ data[ i*3+2 ] = coords[i].z;
+ }
+ writeDoubleArray( out, data );
+ } else if ( ((GeometryArray)node).getCoordRefFloat()!=null ) {
+ out.writeInt( FORMAT_FLOAT );
+ writeFloatArray( out, ((GeometryArray)node).getCoordRefFloat() );
+ } else if ( ((GeometryArray)node).getCoordRefDouble()!=null ) {
+ out.writeInt( FORMAT_DOUBLE );
+ writeDoubleArray( out, ((GeometryArray)node).getCoordRefDouble() );
+ } else out.writeInt( FORMAT_NULL );
+ } else {
+ float[] points = new float[ vertexCount*3 ];
+ ((GeometryArray)node).getCoordinates( 0, points );
+ writeFloatArray( out, points );
+ }
+ }
+
+ if ((vertexFormat & GeometryArray.NORMALS)!=0) {
+ //System.out.println("NORMALS");
+ if ( byRef ) {
+ if ( !(node instanceof IndexedGeometryArray) ) {
+ out.writeInt( ((GeometryArray)node).getInitialNormalIndex() );
+ }
+
+ if ( nio ) {
+ J3DBuffer buf = ((GeometryArray)node).getNormalRefBuffer();
+ if ( BufferWrapper.getBufferType( buf )==BufferWrapper.TYPE_NULL )
+ out.writeInt( FORMAT_NULL );
+ else {
+ out.writeInt( FORMAT_FLOAT );
+ FloatBufferWrapper bb = new FloatBufferWrapper( buf );
+ float[] floats = new float[ bb.limit() ];
+ bb.position( 0 );
+ bb.get( floats );
+ writeFloatArray( out, floats );
+ }
+ } else if ( ((GeometryArray)node).getNormalRef3f()!=null ) {
+ out.writeInt( FORMAT_3F );
+ Vector3f[] norms = ((GeometryArray)node).getNormalRef3f();
+ float[] data = new float[ norms.length*3 ];
+ for (int i = 0 ; i < norms.length ; i++) {
+ data[ i*3+0 ] = norms[i].x;
+ data[ i*3+1 ] = norms[i].y;
+ data[ i*3+2 ] = norms[i].z;
+ }
+ writeFloatArray( out, data );
+ } else if ( ((GeometryArray)node).getNormalRefFloat()!=null ) {
+ out.writeInt( FORMAT_FLOAT );
+ writeFloatArray( out, ((GeometryArray)node).getNormalRefFloat() );
+ } else out.writeInt( FORMAT_NULL );
+ } else {
+ float[] normals = new float[ vertexCount*3 ];
+ ((GeometryArray)node).getNormals( 0, normals );
+ writeFloatArray( out, normals );
+ }
+ }
+
+ if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2)!=0) {
+ //System.out.println("TEXTURE COORDS 2");
+ for(int set=0; set<texCoordSetCount; set++) {
+ if ( byRef ) {
+ if ( !(node instanceof IndexedGeometryArray) ) {
+ out.writeInt( ((GeometryArray)node).getInitialTexCoordIndex( set ) );
+ }
+
+ if ( nio ) {
+ J3DBuffer buf = ((GeometryArray)node).getTexCoordRefBuffer( set );
+ if ( BufferWrapper.getBufferType( buf )==BufferWrapper.TYPE_NULL )
+ out.writeInt( FORMAT_NULL );
+ else {
+ out.writeInt( FORMAT_FLOAT );
+ FloatBufferWrapper bb = new FloatBufferWrapper( buf );
+ float[] floats = new float[ bb.limit() ];
+ bb.position( 0 );
+ bb.get( floats );
+ writeFloatArray( out, floats );
+ }
+ } else if ( ((GeometryArray)node).getTexCoordRef2f(set)!=null ) {
+ out.writeInt( FORMAT_2F );
+ TexCoord2f[] tcoords = ((GeometryArray)node).getTexCoordRef2f(set);
+ float[] data = new float[ tcoords.length*2 ];
+ for (int i = 0 ; i < tcoords.length ; i++) {
+ data[ i*2+0 ] = tcoords[i].x;
+ data[ i*2+1 ] = tcoords[i].y;
+ }
+ writeFloatArray( out, data );
+ } else if ( ((GeometryArray)node).getTexCoordRefFloat(set)!=null ) {
+ out.writeInt( FORMAT_FLOAT );
+ writeFloatArray( out, ((GeometryArray)node).getTexCoordRefFloat(set) );
+ } else out.writeInt( FORMAT_NULL );
+ } else {
+ float[] textureCoords = new float[ vertexCount*2 ];
+ ((GeometryArray)node).getTextureCoordinates( set, 0, textureCoords );
+ writeFloatArray( out, textureCoords );
+ }
+ }
+ } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3)!=0) {
+ //System.out.println("TEXTURE COORDS 3");
+ for(int set=0; set<texCoordSetCount; set++) {
+ if ( byRef ) {
+ if ( !(node instanceof IndexedGeometryArray) ) {
+ out.writeInt( ((GeometryArray)node).getInitialTexCoordIndex( set ) );
+ }
+
+ if ( nio ) {
+ J3DBuffer buf = ((GeometryArray)node).getTexCoordRefBuffer( set );
+ if ( BufferWrapper.getBufferType( buf )==BufferWrapper.TYPE_NULL )
+ out.writeInt( FORMAT_NULL );
+ else {
+ out.writeInt( FORMAT_FLOAT );
+ FloatBufferWrapper bb = new FloatBufferWrapper( buf );
+ float[] floats = new float[ bb.limit() ];
+ bb.position( 0 );
+ bb.get( floats );
+ writeFloatArray( out, floats );
+ }
+ } else if ( ((GeometryArray)node).getTexCoordRef3f(set)!=null ) {
+ out.writeInt( FORMAT_3F );
+ TexCoord3f[] tcoords = ((GeometryArray)node).getTexCoordRef3f(set);
+ float[] data = new float[ tcoords.length*3 ];
+ for (int i = 0 ; i < tcoords.length ; i++) {
+ data[ i*3+0 ] = tcoords[i].x;
+ data[ i*3+1 ] = tcoords[i].y;
+ data[ i*3+2 ] = tcoords[i].z;
+ }
+ writeFloatArray( out, data );
+ } else if ( ((GeometryArray)node).getTexCoordRefFloat(set)!=null ) {
+ out.writeInt( FORMAT_FLOAT );
+ writeFloatArray( out, ((GeometryArray)node).getTexCoordRefFloat(set) );
+ } else out.writeInt( FORMAT_NULL );
+ } else {
+ float[] textureCoords = new float[ vertexCount*3 ];
+ ((GeometryArray)node).getTextureCoordinates( set, 0, textureCoords );
+ writeFloatArray( out, textureCoords );
+ }
+ }
+ } else if ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4)!=0) {
+ //System.out.println("TEXTURE COORDS 4");
+ for(int set=0; set<texCoordSetCount; set++) {
+ if ( byRef ) {
+ if ( !(node instanceof IndexedGeometryArray) ) {
+ out.writeInt( ((GeometryArray)node).getInitialTexCoordIndex( set ) );
+ }
+
+ if ( nio ) {
+ J3DBuffer buf = ((GeometryArray)node).getTexCoordRefBuffer( set );
+ if ( BufferWrapper.getBufferType( buf )==BufferWrapper.TYPE_NULL )
+ out.writeInt( FORMAT_NULL );
+ else {
+ out.writeInt( FORMAT_FLOAT );
+ FloatBufferWrapper bb = new FloatBufferWrapper( buf );
+ float[] floats = new float[ bb.limit() ];
+ bb.position( 0 );
+ bb.get( floats );
+ writeFloatArray( out, floats );
+ }
+ // There is no TexCoordRef4f
+ } else if ( ((GeometryArray)node).getTexCoordRefFloat(set)!=null ) {
+ out.writeInt( FORMAT_FLOAT );
+ writeFloatArray( out, ((GeometryArray)node).getTexCoordRefFloat(set) );
+ } else out.writeInt( FORMAT_NULL );
+ } else {
+ float[] textureCoords = new float[ vertexCount*4 ];
+ ((GeometryArray)node).getTextureCoordinates( set, 0, textureCoords );
+ writeFloatArray( out, textureCoords );
+ }
+ }
+ }
+ }
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ boolean nio = (vertexFormat & GeometryArray.USE_NIO_BUFFER) != 0;
+
+ if ( (vertexFormat & GeometryArray.INTERLEAVED)!=0 ) {
+ if ( !(node instanceof IndexedGeometryArray) ) {
+ ((GeometryArray)node).setInitialVertexIndex( in.readInt() );
+ if ( !(node instanceof GeometryStripArray) )
+ ((GeometryArray)node).setValidVertexCount( in.readInt() );
+ }
+ if ( nio ) {
+ float[] floats = readFloatArray( in );
+ ByteBufferWrapper b =
+ ByteBufferWrapper.allocateDirect( floats.length*4 );
+ FloatBufferWrapper f =
+ b.order( ByteOrderWrapper.nativeOrder() ).asFloatBuffer();
+ f.put( floats );
+ ((GeometryArray)node).setInterleavedVertexBuffer( f.getJ3DBuffer() );
+ } else ((GeometryArray)node).setInterleavedVertices( readFloatArray( in ) );
+ } else {
+ boolean byRef = (vertexFormat & GeometryArray.BY_REFERENCE) != 0;
+
+ // We MUST check for COLOR_4 before we check for COLOR_3,
+ // because the COLOR_3 test will pass for COLOR_4 objects
+
+ if ( !(node instanceof IndexedGeometryArray) ) {
+ if ( !byRef )
+ ((GeometryArray)node).setInitialVertexIndex( in.readInt() );
+ if ( !(node instanceof GeometryStripArray) )
+ ((GeometryArray)node).setValidVertexCount( in.readInt() );
+ }
+
+ if ( (vertexFormat & GeometryArray.COLOR_4)==GeometryArray.COLOR_4 ) {
+ if ( byRef ) {
+ if ( !(node instanceof IndexedGeometryArray) ) {
+ ((GeometryArray)node).setInitialColorIndex( in.readInt() );
+ }
+
+ if ( nio ) {
+ switch( in.readInt() ) {
+ case FORMAT_BYTE: {
+ byte[] bytes = new byte[ in.readInt() ];
+ in.readFully( bytes );
+ ByteBufferWrapper b =
+ ByteBufferWrapper.allocateDirect( bytes.length );
+ b.put( bytes );
+ ((GeometryArray)node).setColorRefBuffer( b.getJ3DBuffer() );
+ }
+ break;
+ case FORMAT_FLOAT: {
+ float[] floats = readFloatArray( in );
+ ByteBufferWrapper b =
+ ByteBufferWrapper.allocateDirect( floats.length*4 );
+ FloatBufferWrapper f =
+ b.order( ByteOrderWrapper.nativeOrder() ).asFloatBuffer();
+ f.put( floats );
+ ((GeometryArray)node).setColorRefBuffer( f.getJ3DBuffer() );
+ }
+ break;
+ }
+ } else {
+ switch( in.readInt() ) {
+ case FORMAT_4F: {
+ float[] data = readFloatArray( in );
+ Color4f[] colors = new Color4f[ data.length/4 ];
+ for (int i = 0 ; i < colors.length ; i++) {
+ colors[i].x = data[ i*4+0 ];
+ colors[i].y = data[ i*4+1 ];
+ colors[i].z = data[ i*4+2 ];
+ colors[i].w = data[ i*4+3 ];
+ }
+ ((GeometryArray)node).setColorRef4f( colors );
+ }
+ break;
+ case FORMAT_FLOAT:
+ ((GeometryArray)node).setColorRefFloat( readFloatArray( in ) );
+ break;
+ case FORMAT_BYTE: {
+ byte[] data = new byte[ in.readInt() ];
+ in.readFully( data );
+ ((GeometryArray)node).setColorRefByte( data );
+ }
+ break;
+ case FORMAT_4B: {
+ Color4b[] colors = new Color4b[ in.readInt() ];
+ byte[] data = new byte[ colors.length*4 ];
+ in.readFully( data );
+ for (int i = 0 ; i < colors.length ; i++) {
+ colors[i].x = data[ i*4+0 ];
+ colors[i].y = data[ i*4+1 ];
+ colors[i].z = data[ i*4+2 ];
+ colors[i].w = data[ i*4+3 ];
+ }
+ ((GeometryArray)node).setColorRef4b( colors );
+ }
+ break;
+ }
+ }
+ } else {
+ // Not by-reference
+ byte[] colors = new byte[ vertexCount*4 ];
+ in.readFully( colors );
+ ((GeometryArray)node).setColors( 0, colors );
+ }
+ } else if ((vertexFormat & GeometryArray.COLOR_3)==GeometryArray.COLOR_3 ) {
+ if ( byRef ) {
+ if ( !(node instanceof IndexedGeometryArray) ) {
+ ((GeometryArray)node).setInitialColorIndex( in.readInt() );
+ }
+
+ if ( nio ) {
+ switch( in.readInt() ) {
+ case FORMAT_BYTE: {
+ byte[] bytes = new byte[ in.readInt() ];
+ in.readFully( bytes );
+ ByteBufferWrapper b =
+ ByteBufferWrapper.allocateDirect( bytes.length );
+ b.put( bytes );
+ ((GeometryArray)node).setColorRefBuffer( b.getJ3DBuffer() );
+ }
+ break;
+ case FORMAT_FLOAT: {
+ float[] floats = readFloatArray( in );
+ ByteBufferWrapper b =
+ ByteBufferWrapper.allocateDirect( floats.length*4 );
+ FloatBufferWrapper f =
+ b.order( ByteOrderWrapper.nativeOrder() ).asFloatBuffer();
+ f.put( floats );
+ ((GeometryArray)node).setColorRefBuffer( f.getJ3DBuffer() );
+ }
+ break;
+ }
+ } else {
+ switch( in.readInt() ) {
+ case FORMAT_3F: {
+ float[] data = readFloatArray( in );
+ Color3f[] colors = new Color3f[ data.length/3 ];
+ for (int i = 0 ; i < colors.length ; i++) {
+ colors[i].x = data[ i*3+0 ];
+ colors[i].y = data[ i*3+1 ];
+ colors[i].z = data[ i*3+2 ];
+ }
+ ((GeometryArray)node).setColorRef3f( colors );
+ }
+ break;
+ case FORMAT_FLOAT:
+ ((GeometryArray)node).setColorRefFloat( readFloatArray( in ) );
+ break;
+ case FORMAT_BYTE: {
+ byte[] data = new byte[ in.readInt() ];
+ in.readFully( data );
+ ((GeometryArray)node).setColorRefByte( data );
+ }
+ break;
+ case FORMAT_3B: {
+ Color3b[] colors = new Color3b[ in.readInt() ];
+ byte[] data = new byte[ colors.length*3 ];
+ in.readFully( data );
+ for (int i = 0 ; i < colors.length ; i++) {
+ colors[i].x = data[ i*3+0 ];
+ colors[i].y = data[ i*3+1 ];
+ colors[i].z = data[ i*3+2 ];
+ }
+ ((GeometryArray)node).setColorRef3b( colors );
+ }
+ break;
+ }
+ }
+ } else {
+ // Not by-reference
+ byte[] colors = new byte[ vertexCount*3 ];
+ in.readFully( colors );
+ ((GeometryArray)node).setColors( 0, colors );
+ }
+ }
+
+ if ((vertexFormat & GeometryArray.COORDINATES)!=0 ) {
+ if ( byRef ) {
+ if ( !(node instanceof IndexedGeometryArray) ) {
+ ((GeometryArray)node).setInitialCoordIndex( in.readInt() );
+ }
+
+ if ( nio ) {
+ switch( in.readInt() ) {
+ case FORMAT_FLOAT: {
+ float[] floats = readFloatArray( in );
+ ByteBufferWrapper b =
+ ByteBufferWrapper.allocateDirect( floats.length*4 );
+ FloatBufferWrapper f =
+ b.order( ByteOrderWrapper.nativeOrder() ).asFloatBuffer();
+ f.put( floats );
+ ((GeometryArray)node).setCoordRefBuffer( f.getJ3DBuffer() );
+ }
+ break;
+ case FORMAT_DOUBLE: {
+ double[] doubles = readDoubleArray( in );
+ ByteBufferWrapper b =
+ ByteBufferWrapper.allocateDirect( doubles.length*4 );
+ DoubleBufferWrapper f =
+ b.order( ByteOrderWrapper.nativeOrder() ).asDoubleBuffer();
+ f.put( doubles );
+ ((GeometryArray)node).setCoordRefBuffer( f.getJ3DBuffer() );
+ }
+ break;
+ }
+ } else {
+ switch( in.readInt() ) {
+ case FORMAT_3F: {
+ float[] data = readFloatArray( in );
+ Point3f[] coords = new Point3f[ data.length/3 ];
+ for (int i = 0 ; i < coords.length ; i++) {
+ coords[i].x = data[ i*3+0 ];
+ coords[i].y = data[ i*3+1 ];
+ coords[i].z = data[ i*3+2 ];
+ }
+ ((GeometryArray)node).setCoordRef3f( coords );
+ }
+ break;
+ case FORMAT_3D: {
+ double[] data = readDoubleArray( in );
+ Point3d[] coords = new Point3d[ data.length/3 ];
+ for (int i = 0 ; i < coords.length ; i++) {
+ coords[i].x = data[ i*3+0 ];
+ coords[i].y = data[ i*3+1 ];
+ coords[i].z = data[ i*3+2 ];
+ }
+ ((GeometryArray)node).setCoordRef3d( coords );
+ }
+ break;
+ case FORMAT_FLOAT:
+ ((GeometryArray)node).setCoordRefFloat( readFloatArray( in ) );
+ break;
+ case FORMAT_DOUBLE:
+ ((GeometryArray)node).setCoordRefDouble( readDoubleArray( in ) );
+ break;
+ }
+ }
+ } else {
+ // Not by-reference
+ float[] points = readFloatArray( in );
+ ((GeometryArray)node).setCoordinates( 0, points );
+ }
+ }
+
+ if ((vertexFormat & GeometryArray.NORMALS)!=0) {
+ if ( byRef ) {
+ if ( !(node instanceof IndexedGeometryArray) ) {
+ ((GeometryArray)node).setInitialNormalIndex( in.readInt() );
+ }
+
+ if ( nio ) {
+ if ( in.readInt() == FORMAT_FLOAT ) {
+ float[] floats = readFloatArray( in );
+ ByteBufferWrapper b =
+ ByteBufferWrapper.allocateDirect( floats.length*4 );
+ FloatBufferWrapper f =
+ b.order( ByteOrderWrapper.nativeOrder() ).asFloatBuffer();
+ f.put( floats );
+ ((GeometryArray)node).setNormalRefBuffer( f.getJ3DBuffer() );
+ }
+ } else {
+ switch( in.readInt() ) {
+ case FORMAT_3F: {
+ float[] data = readFloatArray( in );
+ Vector3f[] norms = new Vector3f[ data.length/3 ];
+ for (int i = 0 ; i < norms.length ; i++) {
+ norms[i].x = data[ i*3+0 ];
+ norms[i].y = data[ i*3+1 ];
+ norms[i].z = data[ i*3+2 ];
+ }
+ ((GeometryArray)node).setNormalRef3f( norms );
+ }
+ break;
+ case FORMAT_FLOAT:
+ ((GeometryArray)node).setNormalRefFloat( readFloatArray( in ) );
+ break;
+ }
+ }
+ } else {
+ // Not by-reference
+ float[] normals = readFloatArray( in );
+ ((GeometryArray)node).setNormals( 0, normals );
+ }
+ }
+
+ if (((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2) != 0) ||
+ ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3) != 0) ||
+ ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4) != 0)) {
+ if ( byRef ) {
+ for(int set=0; set<texCoordSetCount; set++) {
+ if ( !(node instanceof IndexedGeometryArray) ) {
+ ((GeometryArray)node).setInitialTexCoordIndex( set, in.readInt() );
+ }
+
+ if ( nio ) {
+ if ( in.readInt() == FORMAT_FLOAT ) {
+ float[] floats = readFloatArray( in );
+ ByteBufferWrapper b =
+ ByteBufferWrapper.allocateDirect( floats.length*4 );
+ FloatBufferWrapper f = b.order(
+ ByteOrderWrapper.nativeOrder() ).asFloatBuffer();
+ f.put( floats );
+ ((GeometryArray)node).setTexCoordRefBuffer( set,
+ f.getJ3DBuffer() );
+ }
+ } else {
+ switch( in.readInt() ) {
+ case FORMAT_2F: {
+ float[] data = readFloatArray( in );
+ TexCoord2f[] tcoords = new TexCoord2f[ data.length/2 ];
+ for (int i = 0 ; i < tcoords.length ; i++) {
+ tcoords[i].x = data[ i*2+0 ];
+ tcoords[i].y = data[ i*2+1 ];
+ }
+ ((GeometryArray)node).setTexCoordRef2f( set, tcoords );
+ }
+ break;
+ case FORMAT_3F: {
+ float[] data = readFloatArray( in );
+ TexCoord3f[] tcoords = new TexCoord3f[ data.length/3 ];
+ for (int i = 0 ; i < tcoords.length ; i++) {
+ tcoords[i].x = data[ i*3+0 ];
+ tcoords[i].y = data[ i*3+1 ];
+ tcoords[i].z = data[ i*3+2 ];
+ }
+ ((GeometryArray)node).setTexCoordRef3f( set, tcoords );
+ }
+ break;
+ case FORMAT_FLOAT:
+ float[] tcoords = readFloatArray( in );
+ ((GeometryArray)node).setTexCoordRefFloat( set, tcoords );
+ break;
+ }
+ }
+ }
+ } else {
+ // Not by-reference
+ for(int set=0; set<texCoordSetCount; set++) {
+ float[] textureCoords = readFloatArray( in );
+ ((GeometryArray)node).setTextureCoordinates( set, 0, textureCoords );
+ }
+ }
+ }
+ }
+ }
+
+ protected void writeConstructorParams( DataOutput out ) throws
+ IOException {
+ vertexCount = ((GeometryArray)node).getVertexCount();
+ vertexFormat = ((GeometryArray)node).getVertexFormat();
+ texCoordSetCount = ((GeometryArray)node).getTexCoordSetCount();
+ texCoordSetMap = new int[ ((GeometryArray)node).getTexCoordSetMapLength() ];
+
+ ((GeometryArray)node).getTexCoordSetMap( texCoordSetMap );
+
+ out.writeInt(vertexCount);
+ out.writeInt(vertexFormat);
+ out.writeInt( texCoordSetCount );
+ out.writeInt( texCoordSetMap.length );
+ for(int i=0; i<texCoordSetMap.length; i++)
+ out.writeInt( texCoordSetMap[i] );
+ super.writeConstructorParams( out );
+ }
+
+ protected void readConstructorParams( DataInput in ) throws
+ IOException {
+ // Load VertexCount and format first beause
+ // SceneGraphObjectState will call createNode which
+ // requires them
+ vertexCount = in.readInt();
+ vertexFormat = in.readInt();
+ texCoordSetCount = in.readInt();
+ texCoordSetMap = new int[in.readInt()];
+ for(int i=0; i<texCoordSetMap.length; i++)
+ texCoordSetMap[i] = in.readInt();
+
+ super.readConstructorParams( in );
+ }
+
+ protected void writeDoubleArray( DataOutput out, double[] array ) throws IOException {
+
+ // Writing the array into a ByteArray in memory and then dumping
+ // the byte array to DataOutput with a single call is MUCH quicker
+ // than writing each double to DataOutput.
+
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+ DataOutputStream dataOut = new DataOutputStream( byteStream );
+
+ dataOut.writeInt( array.length );
+ for(int i=0; i<array.length; i++)
+ dataOut.writeDouble( array[i] );
+ dataOut.close();
+
+ out.writeInt( byteStream.size() );
+ out.write( byteStream.toByteArray() );
+ }
+
+ protected double[] readDoubleArray( DataInput in ) throws IOException {
+ byte[] buffer = new byte[ in.readInt() ];
+ in.readFully( buffer );
+ ByteArrayInputStream byteStream = new ByteArrayInputStream( buffer );
+ DataInputStream dataIn = new DataInputStream( byteStream );
+
+ double[] array = new double[ dataIn.readInt() ];
+ for(int i=0; i<array.length; i++)
+ array[i] = dataIn.readDouble();
+
+ dataIn.close();
+
+ return array;
+ }
+
+ protected void writeFloatArray( DataOutput out, float[] array ) throws IOException {
+
+ // Writing the array into a ByteArray in memory and then dumping
+ // the byte array to DataOutput with a single call is MUCH quicker
+ // than writing each float to DataOutput.
+
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+ DataOutputStream dataOut = new DataOutputStream( byteStream );
+
+ dataOut.writeInt( array.length );
+ for(int i=0; i<array.length; i++)
+ dataOut.writeFloat( array[i] );
+ dataOut.close();
+
+ out.writeInt( byteStream.size() );
+ out.write( byteStream.toByteArray() );
+ }
+
+ protected float[] readFloatArray( DataInput in ) throws IOException {
+ byte[] buffer = new byte[ in.readInt() ];
+ in.readFully( buffer );
+ ByteArrayInputStream byteStream = new ByteArrayInputStream( buffer );
+ DataInputStream dataIn = new DataInputStream( byteStream );
+
+ float[] array = new float[ dataIn.readInt() ];
+ for(int i=0; i<array.length; i++)
+ array[i] = dataIn.readFloat();
+
+ dataIn.close();
+
+ return array;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/GeometryState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/GeometryState.java
new file mode 100644
index 0000000..5436667
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/GeometryState.java
@@ -0,0 +1,59 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.NodeComponent;
+import javax.media.j3d.Geometry;
+
+public abstract class GeometryState extends NodeComponentState {
+
+ public GeometryState( SymbolTableData symbol, Controller control ) {
+ super(symbol, control);
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/GeometryStripArrayState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/GeometryStripArrayState.java
new file mode 100644
index 0000000..5ebacee
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/GeometryStripArrayState.java
@@ -0,0 +1,81 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.*;
+import javax.media.j3d.GeometryStripArray;
+import javax.media.j3d.SceneGraphObject;
+import javax.vecmath.*;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public abstract class GeometryStripArrayState extends GeometryArrayState {
+
+ protected int[] stripVertexCounts;
+
+ public GeometryStripArrayState( SymbolTableData symbol, Controller control ) {
+ super( symbol, control );
+ }
+
+ protected void writeConstructorParams( DataOutput out ) throws
+ IOException {
+ super.writeConstructorParams( out );
+
+ stripVertexCounts = new int[((GeometryStripArray)node).getNumStrips()];
+ ((GeometryStripArray)node).getStripVertexCounts( stripVertexCounts );
+
+ out.writeInt(stripVertexCounts.length);
+ for(int i=0; i<stripVertexCounts.length; i++)
+ out.writeInt( stripVertexCounts[i] );
+ }
+
+ protected void readConstructorParams( DataInput in ) throws
+ IOException {
+ super.readConstructorParams( in );
+ stripVertexCounts = new int[in.readInt()];
+ for(int i=0; i<stripVertexCounts.length; i++)
+ stripVertexCounts[i] = in.readInt();
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/GroupState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/GroupState.java
new file mode 100644
index 0000000..4ac4fbc
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/GroupState.java
@@ -0,0 +1,142 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.*;
+import java.util.Enumeration;
+import javax.media.j3d.Group;
+import javax.media.j3d.Node;
+import javax.media.j3d.SceneGraphObject;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class GroupState extends NodeState {
+
+ protected SceneGraphObjectState[] groupChildren; // State classes for all the children during load
+
+ public GroupState( SymbolTableData symbol, Controller control ) {
+ super(symbol, control);
+ }
+
+ public void writeObject( DataOutput out) throws IOException {
+ super.writeObject( out );
+
+ control.writeBounds( out, ((Group)node).getCollisionBounds() );
+
+ int numChildren;
+ if (checkProcessChildren())
+ numChildren = ((Group)node).numChildren();
+ else
+ numChildren = 0;
+
+ out.writeInt( numChildren );
+
+ for(int i=0; i<numChildren; i++ ) {
+ control.writeObject( out, control.createState( ((Group)node).getChild(i) ) );
+ }
+
+ out.writeBoolean( ((Group)node).getAlternateCollisionTarget() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ ((Group)node).setCollisionBounds( control.readBounds( in ));
+
+ int numChildren = in.readInt();
+ groupChildren = new SceneGraphObjectState[ numChildren ];
+ for(int i=0; i<numChildren; i++ ) {
+ groupChildren[i] = control.readObject( in );
+ ((Group)node).addChild( (Node)groupChildren[i].getNode() );
+ }
+
+ ((Group)node).setAlternateCollisionTarget( in.readBoolean() );
+ }
+
+ private boolean checkProcessChildren() {
+ if (node instanceof com.sun.j3d.utils.scenegraph.io.SceneGraphIO)
+ return ((com.sun.j3d.utils.scenegraph.io.SceneGraphIO)node).saveChildren();
+ else
+ return processChildren();
+ }
+
+ /**
+ * Returns true if the groups children should be saved.
+ *
+ * This is overridden by 'black box' groups such a geometry primitives
+ *
+ * When users create nodes that implement SceneGraphIO interface then this
+ * method is superceded by saveChildren() in the SceneGraphIO interface
+ */
+ protected boolean processChildren() {
+ return true;
+ }
+
+ public void buildGraph() {
+ for(int i=0; i<groupChildren.length; i++) {
+ if (!groupChildren[i].getSymbol().graphBuilt) {
+ groupChildren[i].getSymbol().graphBuilt = true;
+ groupChildren[i].buildGraph();
+ }
+ }
+ super.buildGraph(); // Must be last call in method
+ }
+
+ public void cleanup() {
+ for(int i=0; i<groupChildren.length; i++) {
+ groupChildren[i].cleanup();
+ groupChildren[i] = null;
+ }
+
+ groupChildren = null;
+ super.cleanup();
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new Group();
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ImageComponent2DState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ImageComponent2DState.java
new file mode 100644
index 0000000..db1a665
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ImageComponent2DState.java
@@ -0,0 +1,114 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.awt.Point;
+import java.awt.image.*;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.ImageComponent2D;
+
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class ImageComponent2DState extends ImageComponentState {
+
+ private BufferedImage bufferedImage;
+
+ public ImageComponent2DState( SymbolTableData symbol, Controller control ) {
+ super( symbol, control );
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws
+ IOException {
+ super.writeConstructorParams( out );
+ ImageComponent2D ic = ((ImageComponent2D)node);
+
+ // If the BufferedImage is associated with the ImageComponent2D by
+ // reference then we don't know much about it, so it'd be hard to
+ // save. So we copy it into an ImageComponent2D and then copy it
+ // out. It comes out in a known format so it's easier to save.
+ if ( ic.isByReference() ) {
+ ImageComponent2D noByRef = new ImageComponent2D(
+ ic.getFormat(), ic.getRenderedImage(), false, ic.isYUp() );
+ bufferedImage = noByRef.getImage();
+ } else bufferedImage = ic.getImage();
+
+ writeBufferedImage( out, bufferedImage );
+ }
+
+ public void readConstructorParams( DataInput in ) throws
+ IOException {
+
+ super.readConstructorParams( in );
+
+ bufferedImage = (BufferedImage)readBufferedImage( in );
+
+ }
+
+ protected SceneGraphObject createNode( Class j3dClass ) {
+ return super.createNode( j3dClass, new Class[] { Integer.TYPE,
+ bufferedImage.getClass(),
+ Boolean.TYPE,
+ Boolean.TYPE },
+ new Object[] { new Integer(format),
+ bufferedImage,
+ new Boolean( byReference ),
+ new Boolean( yUp ) } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new ImageComponent2D( format, bufferedImage, byReference, yUp );
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ImageComponent3DState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ImageComponent3DState.java
new file mode 100644
index 0000000..1811720
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ImageComponent3DState.java
@@ -0,0 +1,139 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.awt.Point;
+import java.awt.image.*;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.ImageComponent3D;
+
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class ImageComponent3DState extends ImageComponentState {
+
+ private BufferedImage[] bufferedImages;
+
+ public ImageComponent3DState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws
+ IOException {
+ super.writeConstructorParams( out );
+
+ ImageComponent3D ic = ((ImageComponent3D)node);
+
+ // If the BufferedImages are associated with the ImageComponent3D by
+ // reference then we don't know much about them, so it'd be hard to
+ // save. So we copy them into an ImageComponent3D and then copy them
+ // out. It comes out in a known format so it's easier to save.
+ if ( ic.isByReference() ) {
+ ImageComponent3D noByRef = new ImageComponent3D(
+ ic.getFormat(), ic.getRenderedImage(), false, ic.isYUp() );
+ bufferedImages = noByRef.getImage();
+ } else bufferedImages = ic.getImage();
+
+ out.writeInt( bufferedImages.length );
+
+ for(int i=0; i<bufferedImages.length; i++) {
+ writeBufferedImage( out, bufferedImages[i] );
+ }
+ }
+
+ public void readConstructorParams( DataInput in ) throws
+ IOException {
+
+ super.readConstructorParams( in );
+
+ bufferedImages = new BufferedImage[ in.readInt() ];
+ for(int i=0; i<bufferedImages.length; i++)
+ bufferedImages[i] = readBufferedImage( in );
+
+ /*
+ // Debug code to show the original images
+ JFrame f = new JFrame();
+ JPanel p = new JPanel() {
+ public void paint(Graphics g ) {
+ super.paint(g);
+ g.drawImage( bufferedImage, 10, 10, null );
+ }
+
+ };
+
+ f.getContentPane().add(p);
+ f.setSize( 200, 200 );
+ f.show();
+ **/
+ }
+
+ protected SceneGraphObject createNode( Class j3dClass ) {
+ return super.createNode( j3dClass, new Class[] { Integer.TYPE,
+ bufferedImages.getClass(),
+ Boolean.TYPE,
+ Boolean.TYPE },
+ new Object[] { new Integer(format),
+ bufferedImages,
+ new Boolean( byReference ),
+ new Boolean( yUp ) } );
+ }
+
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new ImageComponent3D( format, bufferedImages, byReference, yUp );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ImageComponentState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ImageComponentState.java
new file mode 100644
index 0000000..215bb98
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ImageComponentState.java
@@ -0,0 +1,385 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataOutput;
+import java.io.DataInput;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+import java.io.IOException;
+import java.awt.Point;
+import java.awt.image.*;
+import javax.media.j3d.ImageComponent;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import com.sun.j3d.utils.scenegraph.io.retained.SGIORuntimeException;
+import java.awt.color.ColorSpace;
+import java.awt.image.DataBuffer;
+import com.sun.image.codec.jpeg.JPEGImageEncoder;
+import com.sun.image.codec.jpeg.JPEGImageDecoder;
+import com.sun.image.codec.jpeg.JPEGCodec;
+import com.sun.image.codec.jpeg.JPEGEncodeParam;
+
+public abstract class ImageComponentState extends NodeComponentState {
+
+ protected int format;
+ protected int height;
+ protected int width;
+ protected boolean byReference;
+ protected boolean yUp;
+
+ private static final int DIRECT_COLOR_MODEL = 1;
+
+ private static final int SINGLE_PIXEL_PACKED_SAMPLE_MODEL = 1;
+
+ private static final int DATA_BUFFER_INT = 1;
+
+ /**
+ * Do not compress the images
+ */
+ public static final byte NO_COMPRESSION = 0;
+
+ /**
+ * Use GZIP to compress images.
+ *
+ * GZIP decompression is very slow
+ */
+ public static final byte GZIP_COMPRESSION = 1; // GZIP is slow to decompress
+
+ /**
+ * Use JPEG compression for images
+ *
+ * JPEG compression is currently the default. The file format
+ * supports other compression algorithms but there is currently
+ * no API to select the algorithm. This feature is on hold pending
+ * imageio in Java 1.4
+ */
+ public static final byte JPEG_COMPRESSION = 2;
+
+ public ImageComponentState( SymbolTableData symbol, Controller control ) {
+ super( symbol, control );
+ }
+
+
+ protected void writeConstructorParams( DataOutput out ) throws
+ IOException {
+ super.writeConstructorParams( out );
+ out.writeInt( ((ImageComponent)node).getFormat());
+ out.writeInt( ((ImageComponent)node).getHeight());
+ out.writeInt( ((ImageComponent)node).getWidth());
+ out.writeBoolean( ((ImageComponent)node).isByReference() );
+ out.writeBoolean( ((ImageComponent)node).isYUp() );
+ }
+
+ protected void readConstructorParams( DataInput in ) throws
+ IOException {
+ super.readConstructorParams( in );
+ format = in.readInt();
+ height = in.readInt();
+ width = in.readInt();
+ byReference = in.readBoolean();
+ yUp = in.readBoolean();
+ }
+
+ protected void writeBufferedImage( DataOutput out,
+ BufferedImage image ) throws IOException {
+
+ int compressionType = control.getImageCompression();
+
+ out.writeByte( compressionType );
+
+ if (compressionType==NO_COMPRESSION)
+ writeBufferedImageNoCompression( out, image );
+ else if (compressionType==GZIP_COMPRESSION)
+ writeBufferedImageGzipCompression( out, image );
+ else if (compressionType==JPEG_COMPRESSION)
+ writeBufferedImageJpegCompression( out, image );
+ }
+
+ private void writeBufferedImageNoCompression( DataOutput out, BufferedImage image ) throws IOException {
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+ DataOutputStream dataOut = new DataOutputStream( byteStream );
+
+ writeColorModel( dataOut, image.getColorModel() );
+ writeWritableRaster( dataOut, image.getRaster() );
+ dataOut.writeBoolean( image.isAlphaPremultiplied() );
+
+ dataOut.close();
+
+ byte[] buffer = byteStream.toByteArray();
+ out.writeInt( buffer.length );
+ out.write( buffer );
+ }
+
+ private void writeBufferedImageGzipCompression( DataOutput out, BufferedImage image ) throws IOException {
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+ GZIPOutputStream gzipStream = new GZIPOutputStream( byteStream );
+ DataOutputStream dataOut = new DataOutputStream( gzipStream );
+
+ writeColorModel( dataOut, image.getColorModel() );
+ writeWritableRaster( dataOut, image.getRaster() );
+ dataOut.writeBoolean( image.isAlphaPremultiplied() );
+
+ dataOut.flush();
+ gzipStream.finish();
+
+
+ byte[] buffer = byteStream.toByteArray();
+
+ out.writeInt( buffer.length );
+ out.write( buffer);
+ dataOut.close();
+ }
+
+ private void writeBufferedImageJpegCompression( DataOutput out, BufferedImage image ) throws IOException {
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+ JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder( byteStream );
+
+ encoder.encode( image );
+ byteStream.close();
+
+ byte[] buffer = byteStream.toByteArray();
+ out.writeInt( buffer.length );
+ out.write( buffer );
+ }
+
+ protected BufferedImage readBufferedImage( DataInput in ) throws IOException {
+ byte compression = in.readByte();
+
+ if (compression==NO_COMPRESSION)
+ return readBufferedImageNoCompression( in );
+ else if (compression==GZIP_COMPRESSION)
+ return readBufferedImageGzipCompression( in );
+ else if (compression==JPEG_COMPRESSION)
+ return readBufferedImageJpegCompression( in );
+ throw new SGIORuntimeException("Unknown Image Compression");
+ }
+
+ private BufferedImage readBufferedImageNoCompression( DataInput in ) throws IOException {
+ int size = in.readInt();
+ byte[] buffer = new byte[ size ];
+ in.readFully( buffer );
+ ByteArrayInputStream byteIn = new ByteArrayInputStream( buffer );
+ DataInputStream dataIn = new DataInputStream( byteIn );
+
+ ColorModel colorModel = readColorModel( dataIn );
+ WritableRaster raster = readWritableRaster( dataIn );
+ boolean alphaPreMult = dataIn.readBoolean();
+ dataIn.close();
+
+ return new BufferedImage( colorModel, raster, alphaPreMult, null );
+ }
+
+ private BufferedImage readBufferedImageGzipCompression( DataInput in ) throws IOException {
+ int size = in.readInt();
+ byte[] buffer = new byte[ size ];
+ in.readFully( buffer );
+ ByteArrayInputStream byteIn = new ByteArrayInputStream( buffer );
+ GZIPInputStream gzipIn = new GZIPInputStream( byteIn );
+ DataInputStream dataIn = new DataInputStream( gzipIn );
+
+ ColorModel colorModel = readColorModel( dataIn );
+ WritableRaster raster = readWritableRaster( dataIn );
+ boolean alphaPremult = dataIn.readBoolean();
+ dataIn.close();
+
+ return new BufferedImage( colorModel, raster, alphaPremult, null );
+ }
+
+ private BufferedImage readBufferedImageJpegCompression( DataInput in ) throws IOException {
+ int size = in.readInt();
+ byte[] buffer = new byte[ size ];
+ in.readFully( buffer );
+ ByteArrayInputStream byteStream = new ByteArrayInputStream( buffer );
+
+ JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder( byteStream );
+ byteStream.close();
+
+ return decoder.decodeAsBufferedImage();
+ }
+
+ private void writeColorModel( DataOutput out, ColorModel colorModel ) throws IOException {
+ if (colorModel instanceof DirectColorModel) {
+ out.writeInt( DIRECT_COLOR_MODEL );
+ writeDirectColorModel( out, (DirectColorModel)colorModel );
+ } else
+ throw new SGIORuntimeException("Unsupported ColorModel "+colorModel.getClass().getName() );
+ }
+
+ private ColorModel readColorModel( DataInput in ) throws IOException {
+ switch( in.readInt() ) {
+ case DIRECT_COLOR_MODEL:
+ return readDirectColorModel( in );
+ }
+
+ throw new SGIORuntimeException( "Invalid ColorModel - File corrupt" );
+ }
+
+ private void writeDirectColorModel( DataOutput out,
+ DirectColorModel colorModel ) throws IOException {
+ out.writeInt( colorModel.getPixelSize() );
+ out.writeInt( colorModel.getRedMask() );
+ out.writeInt( colorModel.getGreenMask() );
+ out.writeInt( colorModel.getBlueMask() );
+ out.writeInt( colorModel.getAlphaMask() );
+ }
+
+ private DirectColorModel readDirectColorModel( DataInput in ) throws IOException {
+ return new DirectColorModel( in.readInt(),
+ in.readInt(),
+ in.readInt(),
+ in.readInt(),
+ in.readInt() );
+ }
+
+ private void writeWritableRaster( DataOutput out, WritableRaster raster ) throws IOException{
+ writeSampleModel( out, raster.getSampleModel() );
+ writeDataBuffer( out, raster.getDataBuffer() );
+ Point origin = new Point();
+ // TODO Get the origin of the raster - seems to be missing from the raster API
+ out.writeInt( origin.x );
+ out.writeInt( origin.y );
+ }
+
+ private WritableRaster readWritableRaster( DataInput in ) throws IOException {
+ return Raster.createWritableRaster( readSampleModel( in ),
+ readDataBuffer( in ),
+ new Point( in.readInt(), in.readInt() ));
+ }
+
+ private void writeSampleModel( DataOutput out, SampleModel model ) throws IOException {
+ if (model instanceof SinglePixelPackedSampleModel) {
+ out.writeInt( SINGLE_PIXEL_PACKED_SAMPLE_MODEL );
+ writeSinglePixelPackedSampleModel( out, (SinglePixelPackedSampleModel)model );
+ } else
+ throw new SGIORuntimeException("Unsupported SampleModel "+model.getClass().getName() );
+ }
+
+ private SampleModel readSampleModel( DataInput in ) throws IOException {
+ switch( in.readInt() ) {
+ case SINGLE_PIXEL_PACKED_SAMPLE_MODEL:
+ return readSinglePixelPackedSampleModel( in );
+ }
+
+ throw new SGIORuntimeException("Invalid SampleModel - file corrupt");
+ }
+
+ private void writeSinglePixelPackedSampleModel( DataOutput out,
+ SinglePixelPackedSampleModel model ) throws IOException {
+
+ int[] masks = model.getBitMasks();
+ out.writeInt( masks.length );
+ for(int i=0; i<masks.length; i++)
+ out.writeInt( masks[i] );
+
+ out.writeInt( model.getDataType() );
+ out.writeInt( model.getWidth() );
+ out.writeInt( model.getHeight() );
+ out.writeInt( model.getScanlineStride() );
+
+ }
+
+ private SinglePixelPackedSampleModel readSinglePixelPackedSampleModel( DataInput in )
+ throws IOException {
+
+ int masks[] = new int[ in.readInt() ];
+ for(int i=0; i<masks.length; i++)
+ masks[i] = in.readInt();
+
+ return new SinglePixelPackedSampleModel( in.readInt(),
+ in.readInt(),
+ in.readInt(),
+ in.readInt(),
+ masks );
+ }
+
+ private void writeDataBuffer( DataOutput out, DataBuffer buffer ) throws IOException {
+ if (buffer instanceof DataBufferInt) {
+ out.writeInt( DATA_BUFFER_INT );
+ writeDataBufferInt( out, (DataBufferInt)buffer );
+ } else
+ throw new SGIORuntimeException("Unsupported DataBuffer "+buffer.getClass().getName() );
+ }
+
+ private DataBuffer readDataBuffer( DataInput in ) throws IOException {
+ switch( in.readInt() ) {
+ case DATA_BUFFER_INT:
+ return readDataBufferInt( in );
+ }
+
+ throw new SGIORuntimeException("Incorrect DataBuffer - file corrupt");
+ }
+
+ private void writeDataBufferInt( DataOutput out, DataBufferInt buffer ) throws IOException {
+ int[][] data = buffer.getBankData();
+ out.writeInt( data.length );
+ for(int i=0; i<data.length; i++) {
+ out.writeInt( data[i].length );
+ for( int j=0; j<data[i].length; j++)
+ out.writeInt( data[i][j] );
+ }
+
+ out.writeInt( buffer.getSize() );
+
+ // TODO Handle DataBufferInt offsets
+
+ }
+
+ private DataBufferInt readDataBufferInt( DataInput in ) throws IOException {
+ int[][] data = new int[in.readInt()][];
+ for(int i=0; i<data.length; i++) {
+ data[i] = new int[ in.readInt() ];
+ for( int j=0; j<data[i].length; j++)
+ data[i][j] = in.readInt();
+ }
+
+
+ return new DataBufferInt( data, in.readInt() );
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedGeometryArrayState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedGeometryArrayState.java
new file mode 100644
index 0000000..fc88c36
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedGeometryArrayState.java
@@ -0,0 +1,176 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.*;
+import javax.media.j3d.IndexedGeometryArray;
+import javax.media.j3d.IndexedGeometryStripArray;
+import javax.media.j3d.GeometryArray;
+import javax.media.j3d.SceneGraphObject;
+import javax.vecmath.*;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public abstract class IndexedGeometryArrayState extends GeometryArrayState {
+
+ protected int indexCount;
+
+ public IndexedGeometryArrayState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ int[] indices = new int[ ((IndexedGeometryArray)node).getIndexCount() ];
+
+ boolean coordOnly = (vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) !=0;
+
+ if ( (((vertexFormat & GeometryArray.COLOR_3)!=0) ||
+ ((vertexFormat & GeometryArray.COLOR_4)!=0)) && !coordOnly ) {
+ ((IndexedGeometryArray)node).getColorIndices( 0, indices );
+ writeIntArray( out, indices );
+ }
+
+ if ( (vertexFormat & GeometryArray.COORDINATES)!=0 ) {
+ ((IndexedGeometryArray)node).getCoordinateIndices( 0, indices );
+ writeIntArray( out, indices );
+ }
+
+ if ( ((vertexFormat & GeometryArray.NORMALS)!=0) && !coordOnly) {
+ ((IndexedGeometryArray)node).getNormalIndices( 0, indices );
+ writeIntArray( out, indices );
+ }
+
+ if ( (((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2)!=0) ||
+ ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3)!=0) ||
+ ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4)!=0)) && !coordOnly ) {
+ for(int i=0; i<((IndexedGeometryArray)node).getTexCoordSetCount(); i++) {
+ ((IndexedGeometryArray)node).getTextureCoordinateIndices( i, 0, indices );
+ writeIntArray( out, indices );
+ }
+ }
+
+ if ( !(node instanceof IndexedGeometryStripArray) ) {
+ out.writeInt( ((IndexedGeometryArray)node).getValidIndexCount() );
+ }
+ out.writeInt( ((IndexedGeometryArray)node).getInitialIndexIndex() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ int[] indices = new int[indexCount];
+
+ boolean coordOnly = (vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY) != 0;
+
+ if ( (((vertexFormat & GeometryArray.COLOR_3)!=0) ||
+ ((vertexFormat & GeometryArray.COLOR_4)!=0)) && !coordOnly ) {
+ readIntArray( in, indices );
+ ((IndexedGeometryArray)node).setColorIndices( 0, indices );
+ }
+
+ if ( (vertexFormat & GeometryArray.COORDINATES)!=0 ) {
+ readIntArray( in, indices );
+ ((IndexedGeometryArray)node).setCoordinateIndices( 0, indices );
+ }
+
+ if ( ((vertexFormat & GeometryArray.NORMALS)!=0) && !coordOnly ) {
+ readIntArray( in, indices );
+ ((IndexedGeometryArray)node).setNormalIndices( 0, indices );
+ }
+
+ if ( (((vertexFormat & GeometryArray.TEXTURE_COORDINATE_2)!=0) ||
+ ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_3)!=0) ||
+ ((vertexFormat & GeometryArray.TEXTURE_COORDINATE_4)!=0)) && !coordOnly ) {
+ for(int i=0; i<texCoordSetCount; i++) {
+ readIntArray( in, indices );
+ ((IndexedGeometryArray)node).setTextureCoordinateIndices( i, 0, indices );
+ }
+ }
+
+ if ( !(node instanceof IndexedGeometryStripArray) ) {
+ ((IndexedGeometryArray)node).setValidIndexCount( in.readInt() );
+ }
+ ((IndexedGeometryArray)node).setInitialIndexIndex( in.readInt() );
+ }
+
+ protected void writeConstructorParams( DataOutput out ) throws
+ IOException {
+ super.writeConstructorParams( out );
+ out.writeInt( ((IndexedGeometryArray)node).getIndexCount() );
+ }
+
+ protected void readConstructorParams( DataInput in ) throws
+ IOException {
+ super.readConstructorParams( in );
+ indexCount = in.readInt();
+ }
+
+ protected void writeIntArray( DataOutput out, int[] array ) throws IOException {
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+ DataOutputStream dataOut = new DataOutputStream( byteStream );
+
+ for(int i=0; i<array.length; i++)
+ dataOut.writeInt( array[i] );
+ dataOut.close();
+
+ out.writeInt( byteStream.size() );
+ out.write( byteStream.toByteArray() );
+ }
+
+ private void readIntArray( DataInput in, int[] array ) throws IOException {
+ byte[] buffer = new byte[ in.readInt() ];
+ in.readFully( buffer );
+ ByteArrayInputStream byteStream = new ByteArrayInputStream( buffer );
+ DataInputStream dataIn = new DataInputStream( byteStream );
+
+ for(int i=0; i<array.length; i++)
+ array[i] = dataIn.readInt();
+
+ dataIn.close();
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedGeometryStripArrayState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedGeometryStripArrayState.java
new file mode 100644
index 0000000..c37183f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedGeometryStripArrayState.java
@@ -0,0 +1,81 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.*;
+import javax.media.j3d.IndexedGeometryStripArray;
+import javax.media.j3d.SceneGraphObject;
+import javax.vecmath.*;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public abstract class IndexedGeometryStripArrayState extends IndexedGeometryArrayState {
+
+ protected int[] stripIndexCounts;
+
+ public IndexedGeometryStripArrayState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ protected void writeConstructorParams( DataOutput out ) throws
+ IOException {
+ super.writeConstructorParams( out );
+
+ stripIndexCounts = new int[((IndexedGeometryStripArray)node).getNumStrips()];
+ ((IndexedGeometryStripArray)node).getStripIndexCounts( stripIndexCounts );
+
+ out.writeInt(stripIndexCounts.length);
+ for(int i=0; i<stripIndexCounts.length; i++)
+ out.writeInt( stripIndexCounts[i] );
+ }
+
+ protected void readConstructorParams( DataInput in ) throws
+ IOException {
+ super.readConstructorParams( in );
+ stripIndexCounts = new int[in.readInt()];
+ for(int i=0; i<stripIndexCounts.length; i++)
+ stripIndexCounts[i] = in.readInt();
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedLineArrayState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedLineArrayState.java
new file mode 100644
index 0000000..b1bf84f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedLineArrayState.java
@@ -0,0 +1,92 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.IndexedLineArray;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class IndexedLineArrayState extends IndexedGeometryArrayState {
+
+ public IndexedLineArrayState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ }
+
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return super.createNode( j3dClass, new Class[] { Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ texCoordSetMap.getClass(),
+ Integer.TYPE },
+ new Object[] { new Integer( vertexCount ),
+ new Integer( vertexFormat ),
+ new Integer( texCoordSetCount ),
+ texCoordSetMap,
+ new Integer( indexCount ) } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new IndexedLineArray( vertexCount, vertexFormat, texCoordSetCount, texCoordSetMap, indexCount );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedLineStripArrayState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedLineStripArrayState.java
new file mode 100644
index 0000000..8715ae2
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedLineStripArrayState.java
@@ -0,0 +1,86 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.IndexedLineStripArray;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class IndexedLineStripArrayState extends IndexedGeometryStripArrayState {
+
+ /** Creates new BranchGroupState */
+ public IndexedLineStripArrayState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return super.createNode( j3dClass, new Class[] { Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ texCoordSetMap.getClass(),
+ Integer.TYPE,
+ stripIndexCounts.getClass() },
+ new Object[] { new Integer( vertexCount ),
+ new Integer( vertexFormat ),
+ new Integer( texCoordSetCount ),
+ texCoordSetMap,
+ new Integer( indexCount ),
+ stripIndexCounts } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new IndexedLineStripArray( vertexCount, vertexFormat, texCoordSetCount, texCoordSetMap, indexCount, stripIndexCounts );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedPointArrayState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedPointArrayState.java
new file mode 100644
index 0000000..eef24af
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedPointArrayState.java
@@ -0,0 +1,93 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.IndexedPointArray;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class IndexedPointArrayState extends IndexedGeometryArrayState {
+
+ /** Creates new BranchGroupState */
+ public IndexedPointArrayState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ }
+
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return super.createNode( j3dClass, new Class[] { Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ texCoordSetMap.getClass(),
+ Integer.TYPE },
+ new Object[] { new Integer( vertexCount ),
+ new Integer( vertexFormat ),
+ new Integer( texCoordSetCount ),
+ texCoordSetMap,
+ new Integer( indexCount ) } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new IndexedPointArray( vertexCount, vertexFormat, texCoordSetCount, texCoordSetMap, indexCount );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedQuadArrayState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedQuadArrayState.java
new file mode 100644
index 0000000..5d70737
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedQuadArrayState.java
@@ -0,0 +1,92 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.IndexedQuadArray;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class IndexedQuadArrayState extends IndexedGeometryArrayState {
+
+ public IndexedQuadArrayState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ }
+
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return super.createNode( j3dClass, new Class[] { Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ texCoordSetMap.getClass(),
+ Integer.TYPE },
+ new Object[] { new Integer( vertexCount ),
+ new Integer( vertexFormat ),
+ new Integer( texCoordSetCount ),
+ texCoordSetMap,
+ new Integer( indexCount ) } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new IndexedQuadArray( vertexCount, vertexFormat, texCoordSetCount, texCoordSetMap, indexCount );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedTriangleArrayState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedTriangleArrayState.java
new file mode 100644
index 0000000..dbcc364
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedTriangleArrayState.java
@@ -0,0 +1,92 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.IndexedTriangleArray;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class IndexedTriangleArrayState extends IndexedGeometryArrayState {
+
+ public IndexedTriangleArrayState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ }
+
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return super.createNode( j3dClass, new Class[] { Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ texCoordSetMap.getClass(),
+ Integer.TYPE },
+ new Object[] { new Integer( vertexCount ),
+ new Integer( vertexFormat ),
+ new Integer( texCoordSetCount ),
+ texCoordSetMap,
+ new Integer( indexCount ) } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new IndexedTriangleArray( vertexCount, vertexFormat, texCoordSetCount, texCoordSetMap, indexCount );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedTriangleFanArrayState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedTriangleFanArrayState.java
new file mode 100644
index 0000000..0130d26
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedTriangleFanArrayState.java
@@ -0,0 +1,95 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.IndexedTriangleFanArray;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class IndexedTriangleFanArrayState extends IndexedGeometryStripArrayState {
+
+ /** Creates new BranchGroupState */
+ public IndexedTriangleFanArrayState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ }
+
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return super.createNode( j3dClass, new Class[] { Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ texCoordSetMap.getClass(),
+ Integer.TYPE,
+ stripIndexCounts.getClass() },
+ new Object[] { new Integer( vertexCount ),
+ new Integer( vertexFormat ),
+ new Integer( texCoordSetCount ),
+ texCoordSetMap,
+ new Integer( indexCount ),
+ stripIndexCounts } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new IndexedTriangleFanArray( vertexCount, vertexFormat, texCoordSetCount, texCoordSetMap, indexCount, stripIndexCounts );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedTriangleStripArrayState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedTriangleStripArrayState.java
new file mode 100644
index 0000000..7fad783
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/IndexedTriangleStripArrayState.java
@@ -0,0 +1,95 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.IndexedTriangleStripArray;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class IndexedTriangleStripArrayState extends IndexedGeometryStripArrayState {
+
+ /** Creates new BranchGroupState */
+ public IndexedTriangleStripArrayState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ }
+
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return super.createNode( j3dClass, new Class[] { Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ texCoordSetMap.getClass(),
+ Integer.TYPE,
+ stripIndexCounts.getClass() },
+ new Object[] { new Integer( vertexCount ),
+ new Integer( vertexFormat ),
+ new Integer( texCoordSetCount ),
+ texCoordSetMap,
+ new Integer( indexCount ),
+ stripIndexCounts } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new IndexedTriangleStripArray( vertexCount, vertexFormat, texCoordSetCount, texCoordSetMap, indexCount, stripIndexCounts );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/InterpolatorState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/InterpolatorState.java
new file mode 100644
index 0000000..8466aa2
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/InterpolatorState.java
@@ -0,0 +1,88 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.Interpolator;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Alpha;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public abstract class InterpolatorState extends BehaviorState {
+
+ private int alpha=0;
+
+ public InterpolatorState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ out.writeInt( control.getSymbolTable().addReference( ((Interpolator)node).getAlpha() ));
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ alpha = in.readInt();
+ }
+
+ /**
+ * Called when this component reference count is incremented.
+ * Allows this component to update the reference count of any components
+ * that it references.
+ */
+ public void addSubReference() {
+ control.getSymbolTable().incNodeComponentRefCount( alpha );
+ }
+
+ public void buildGraph() {
+ ((Interpolator)node).setAlpha( (Alpha)control.getSymbolTable().getJ3dNode( alpha ));
+ super.buildGraph(); // Must be last call in method
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LODState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LODState.java
new file mode 100644
index 0000000..3b7ef93
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LODState.java
@@ -0,0 +1,95 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Switch;
+import javax.media.j3d.LOD;
+
+public abstract class LODState extends BehaviorState {
+
+ private int[] switches;
+
+ public LODState(SymbolTableData symbol,Controller control) {
+ super(symbol, control);
+
+ if (node!=null) {
+ switches = new int[ ((LOD)node).numSwitches() ];
+ for( int i=0; i<switches.length; i++)
+ switches[i] = control.getSymbolTable().addReference( ((LOD)node).getSwitch(i) );
+ }
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeInt( switches.length );
+ for( int i=0; i<switches.length; i++)
+ out.writeInt( switches[i] );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ LOD attr = (LOD)node;
+
+ switches = new int[ in.readInt() ];
+ for( int i=0; i<switches.length; i++)
+ switches[i] = in.readInt();
+ }
+
+ public void buildGraph() {
+ LOD attr = (LOD)node;
+ for(int i=0; i<switches.length; i++)
+ attr.addSwitch( (Switch)control.getSymbolTable().getJ3dNode( switches[i] ) );
+ super.buildGraph(); // Must be last call in method
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LeafState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LeafState.java
new file mode 100644
index 0000000..2ec67e9
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LeafState.java
@@ -0,0 +1,58 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import javax.media.j3d.Leaf;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public abstract class LeafState extends NodeState {
+
+ /** Creates new BranchGroupState */
+ public LeafState( SymbolTableData symbol, Controller control ) {
+ super( symbol, control );
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LightState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LightState.java
new file mode 100644
index 0000000..6ea80fc
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LightState.java
@@ -0,0 +1,113 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.*;
+import javax.media.j3d.Light;
+import javax.media.j3d.BoundingLeaf;
+import javax.media.j3d.Group;
+import javax.vecmath.Color3f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public abstract class LightState extends LeafState {
+
+ private int boundingLeaf = 0;
+ private int[] scope;
+
+ public LightState( SymbolTableData symbol, Controller control ) {
+ super( symbol, control );
+
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ scope = new int[ ((Light)node).numScopes() ];
+ for(int i=0; i<((Light)node).numScopes(); i++) {
+ scope[i] = control.getSymbolTable().addReference( ((Light)node).getScope(i) );;
+ }
+ boundingLeaf = control.getSymbolTable().addReference( ((Light)node).getInfluencingBoundingLeaf() );
+
+ Color3f color = new Color3f();
+ ((Light)node).getColor( color );
+ control.writeColor3f( out, color );
+
+ out.writeBoolean( ((Light)node).getEnable() );
+
+ out.writeInt( boundingLeaf );
+ control.writeBounds( out, ((Light)node).getInfluencingBounds() );
+
+ out.writeInt( scope.length );
+ for( int i=0; i<scope.length; i++) {
+ out.writeInt( scope[i] );
+ }
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject(in);
+ ((Light)node).setColor( control.readColor3f(in) );
+ ((Light)node).setEnable( in.readBoolean() );
+
+ boundingLeaf = in.readInt();
+
+ ((Light)node).setInfluencingBounds( control.readBounds(in) );
+
+ scope = new int[in.readInt()];
+ for(int i=0; i<scope.length; i++) {
+ scope[i] = in.readInt();
+ }
+
+ }
+
+ public void buildGraph() {
+ ((Light)node).setInfluencingBoundingLeaf( (BoundingLeaf)control.getSymbolTable().getJ3dNode( boundingLeaf ));
+ for(int i=0; i<scope.length; i++) {
+ ((Light)node).addScope( (Group)control.getSymbolTable().getJ3dNode( scope[i] ));
+ }
+ super.buildGraph(); // Must be last call in method
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LineArrayState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LineArrayState.java
new file mode 100644
index 0000000..b895090
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LineArrayState.java
@@ -0,0 +1,76 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import javax.media.j3d.LineArray;
+import javax.media.j3d.SceneGraphObject;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class LineArrayState extends GeometryArrayState {
+
+ public LineArrayState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] {
+ Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ texCoordSetMap.getClass()
+ },
+ new Object[] { new Integer( vertexCount ),
+ new Integer( vertexFormat ),
+ new Integer( texCoordSetCount ),
+ texCoordSetMap } );
+ }
+
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new LineArray( vertexCount, vertexFormat, texCoordSetCount, texCoordSetMap );
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LineAttributesState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LineAttributesState.java
new file mode 100644
index 0000000..586db51
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LineAttributesState.java
@@ -0,0 +1,87 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.LineAttributes;
+
+public class LineAttributesState extends NodeComponentState {
+
+ public LineAttributesState( SymbolTableData symbol, Controller control ) {
+ super(symbol, control);
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ LineAttributes attr = (LineAttributes)node;
+ out.writeBoolean( attr.getLineAntialiasingEnable() );
+ out.writeInt( attr.getLinePattern() );
+ out.writeFloat( attr.getLineWidth() );
+ out.writeInt( attr.getPatternMask() );
+ out.writeInt( attr.getPatternScaleFactor() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ LineAttributes attr = (LineAttributes)node;
+ attr.setLineAntialiasingEnable( in.readBoolean() );
+ attr.setLinePattern( in.readInt() );
+ attr.setLineWidth( in.readFloat() );
+ attr.setPatternMask( in.readInt() );
+ attr.setPatternScaleFactor( in.readInt() );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new LineAttributes();
+ }
+
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LineStripArrayState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LineStripArrayState.java
new file mode 100644
index 0000000..69905c0
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LineStripArrayState.java
@@ -0,0 +1,78 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import javax.media.j3d.LineStripArray;
+import javax.media.j3d.SceneGraphObject;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class LineStripArrayState extends GeometryStripArrayState {
+
+ public LineStripArrayState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] {
+ Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ texCoordSetMap.getClass(),
+ stripVertexCounts.getClass()
+ },
+ new Object[] { new Integer( vertexCount ),
+ new Integer( vertexFormat ),
+ new Integer( texCoordSetCount ),
+ texCoordSetMap,
+ stripVertexCounts } );
+ }
+
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new LineStripArray( vertexCount, vertexFormat, texCoordSetCount, texCoordSetMap, stripVertexCounts );
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LinearFogState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LinearFogState.java
new file mode 100644
index 0000000..02ba6de
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LinearFogState.java
@@ -0,0 +1,80 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.LinearFog;
+import javax.media.j3d.SceneGraphObject;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class LinearFogState extends FogState {
+
+ public LinearFogState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeDouble( ((LinearFog)node).getBackDistance() );
+ out.writeDouble( ((LinearFog)node).getFrontDistance() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ ((LinearFog)node).setBackDistance( in.readDouble() );
+ ((LinearFog)node).setFrontDistance( in.readDouble() );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new LinearFog();
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LinkState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LinkState.java
new file mode 100644
index 0000000..70ff3b5
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/LinkState.java
@@ -0,0 +1,103 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import javax.media.j3d.Link;
+import javax.media.j3d.SharedGroup;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class LinkState extends LeafState {
+
+ private int sharedGroup;
+ private SymbolTableData sharedGroupSymbol;
+
+ /** Creates new BranchGroupState */
+ public LinkState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ if (node!=null) {
+ SharedGroup sg = ((Link)node).getSharedGroup();
+ sharedGroup = control.getSymbolTable().addReference( sg );
+ sharedGroupSymbol = control.getSymbolTable().getSymbol( sharedGroup );
+ }
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ if (sharedGroupSymbol.nodeState==null) {
+ out.writeInt( -1 );
+ SharedGroup sg = ((Link)node).getSharedGroup();
+ control.writeSharedGroup( out, sg, sharedGroupSymbol );
+ } else {
+ out.writeInt( sharedGroupSymbol.nodeID );
+ }
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ sharedGroup = in.readInt();
+
+ if (sharedGroup==-1) {
+ sharedGroup = control.readSharedGroup( in );
+ }
+ }
+
+ public void buildGraph() {
+ sharedGroupSymbol = control.getSymbolTable().getSymbol( sharedGroup );
+ ((SharedGroupState)sharedGroupSymbol.nodeState).buildGraph();
+ ((Link)node).setSharedGroup( (SharedGroup)sharedGroupSymbol.j3dNode );
+ super.buildGraph(); // Must be last call in method
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new Link();
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/MaterialState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/MaterialState.java
new file mode 100644
index 0000000..19a9030
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/MaterialState.java
@@ -0,0 +1,96 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Material;
+import javax.vecmath.Color3f;
+
+public class MaterialState extends NodeComponentState {
+
+ public MaterialState( SymbolTableData symbol, Controller control ) {
+ super(symbol, control);
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ Material attr = (Material)node;
+ Color3f clr = new Color3f();
+ attr.getAmbientColor( clr );
+ control.writeColor3f( out, clr );
+ attr.getDiffuseColor( clr );
+ control.writeColor3f( out, clr );
+ attr.getEmissiveColor( clr );
+ control.writeColor3f( out, clr );
+ attr.getSpecularColor( clr );
+ control.writeColor3f( out, clr );
+
+ out.writeBoolean( attr.getLightingEnable() );
+ out.writeInt( attr.getColorTarget() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ Material attr = (Material)node;
+ attr.setAmbientColor( control.readColor3f( in ) );
+ attr.setDiffuseColor( control.readColor3f( in ) );
+ attr.setEmissiveColor( control.readColor3f( in ) );
+ attr.setSpecularColor( control.readColor3f( in ) );
+ attr.setLightingEnable( in.readBoolean() );
+ attr.setColorTarget( in.readInt() );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new Material();
+ }
+
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/MediaContainerState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/MediaContainerState.java
new file mode 100644
index 0000000..be8ce25
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/MediaContainerState.java
@@ -0,0 +1,80 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.MediaContainer;
+import javax.media.j3d.SceneGraphObject;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class MediaContainerState extends NodeComponentState {
+
+ public MediaContainerState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeBoolean( ((MediaContainer)node).getCacheEnable() );
+ out.writeUTF( ((MediaContainer)node).getURLString() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ ((MediaContainer)node).setCacheEnable( in.readBoolean() );
+ ((MediaContainer)node).setURLString( in.readUTF() );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new MediaContainer();
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ModelClipState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ModelClipState.java
new file mode 100644
index 0000000..e441bcb
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ModelClipState.java
@@ -0,0 +1,133 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.ModelClip;
+import javax.media.j3d.BoundingLeaf;
+import javax.media.j3d.Group;
+import javax.vecmath.Vector4d;
+
+public class ModelClipState extends LeafState {
+
+ private int[] scopes;
+ private int influencingBoundingLeaf;
+
+ public ModelClipState(SymbolTableData symbol,Controller control) {
+ super(symbol, control);
+
+ if (node!=null) {
+ scopes = new int[ ((ModelClip)node).numScopes() ];
+ for(int i=0; i<scopes.length; i++)
+ scopes[i] = control.getSymbolTable().addReference( ((ModelClip)node).getScope(i));
+
+ influencingBoundingLeaf = control.getSymbolTable().addReference( ((ModelClip)node).getInfluencingBoundingLeaf());
+ }
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ boolean[] enables = new boolean[6];
+ ((ModelClip)node).getEnables( enables );
+
+ out.writeInt( scopes.length );
+ for(int i=0; i<scopes.length; i++)
+ out.writeInt( scopes[i] );
+
+ out.writeInt( influencingBoundingLeaf );
+ control.writeBounds( out, ((ModelClip)node).getInfluencingBounds() );
+
+ Vector4d[] planes = new Vector4d[6];
+ ((ModelClip)node).getPlanes( planes );
+
+ for(int i=0; i<6; i++) {
+ out.writeBoolean( enables[i] );
+ control.writeVector4d( out, planes[i] );
+ }
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ scopes = new int[ in.readInt() ];
+ for(int i=0; i<scopes.length; i++)
+ scopes[i] = in.readInt();
+
+ influencingBoundingLeaf = in.readInt();
+
+ ((ModelClip)node).setInfluencingBounds( control.readBounds(in));
+
+ boolean[] enables = new boolean[6];
+ Vector4d[] planes = new Vector4d[6];
+ for( int i=0; i<6; i++) {
+ enables[i] = in.readBoolean();
+ planes[i] = control.readVector4d( in );
+ }
+
+ ((ModelClip)node).setEnables( enables );
+ ((ModelClip)node).setPlanes( planes );
+ }
+
+ public void buildGraph() {
+ for(int i=0; i<scopes.length; i++)
+ ((ModelClip)node).addScope( (Group)control.getSymbolTable().getJ3dNode( scopes[i] ));
+
+ ((ModelClip)node).setInfluencingBoundingLeaf( (BoundingLeaf)control.getSymbolTable().getJ3dNode( influencingBoundingLeaf ));
+ super.buildGraph();
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new ModelClip();
+ }
+
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/MorphState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/MorphState.java
new file mode 100644
index 0000000..90c3fb9
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/MorphState.java
@@ -0,0 +1,134 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.Morph;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.GeometryArray;
+import javax.media.j3d.Appearance;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class MorphState extends LeafState {
+
+ private int[] geometry;
+ private double[] weights;
+ private int appearance;
+
+ public MorphState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+
+ if (node!=null) {
+ appearance = control.getSymbolTable().addReference( ((Morph)node).getAppearance() );
+ weights = ((Morph)node).getWeights();
+ geometry = new int[weights.length];
+ for(int i=0; i<weights.length; i++)
+ geometry[i] = control.getSymbolTable().addReference( ((Morph)node).getGeometryArray(i) );
+ }
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ control.writeBounds( out, ((Morph)node).getCollisionBounds() );
+
+ out.writeInt( appearance );
+ out.writeBoolean( ((Morph)node).getAppearanceOverrideEnable() );
+
+ out.writeInt( geometry.length );
+ for(int i=0; i<geometry.length; i++) {
+ out.writeInt( geometry[i] );
+ out.writeDouble( weights[i] );
+ }
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ ((Morph)node).setCollisionBounds( control.readBounds( in ));
+ appearance = in.readInt();
+ ((Morph)node).setAppearanceOverrideEnable( in.readBoolean() );
+
+ int len = in.readInt();
+ geometry = new int[len];
+ weights = new double[len];
+ for(int i=0; i<geometry.length; i++) {
+ geometry[i] = in.readInt();
+ weights[i] = in.readDouble();
+ }
+ }
+
+ /**
+ * Called when this component reference count is incremented.
+ * Allows this component to update the reference count of any components
+ * that it references.
+ */
+ public void addSubReference() {
+ control.getSymbolTable().incNodeComponentRefCount( appearance );
+ for(int i=0; i<geometry.length; i++)
+ control.getSymbolTable().incNodeComponentRefCount( geometry[ i ] );
+ }
+
+ public void buildGraph() {
+ ((Morph)node).setAppearance( (Appearance)control.getSymbolTable().getJ3dNode( appearance ) );
+
+ GeometryArray[] geom = new GeometryArray[ geometry.length ];
+ for(int i=0; i<geometry.length; i++) {
+ geom[i] = (GeometryArray)control.getSymbolTable().getJ3dNode( geometry[i] );
+ }
+
+ ((Morph)node).setGeometryArrays( geom );
+ ((Morph)node).setWeights( weights );
+ super.buildGraph(); // Must be last call in method
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new Morph( null );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/NodeComponentState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/NodeComponentState.java
new file mode 100644
index 0000000..84e7318
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/NodeComponentState.java
@@ -0,0 +1,85 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataOutput;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.NodeComponent;
+import javax.media.j3d.SceneGraphObject;
+
+public abstract class NodeComponentState extends SceneGraphObjectState {
+
+ public NodeComponentState( SymbolTableData symbol, Controller control ) {
+ super(symbol, control);
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ // This fix was added in file version 2
+ if (control.getCurrentFileVersion()>1)
+ ((NodeComponent)this.node).setDuplicateOnCloneTree(in.readBoolean());
+
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject(out);
+ out.writeBoolean(((NodeComponent)this.node).getDuplicateOnCloneTree());
+ }
+
+ /**
+ * Called when this component reference count is incremented.
+ * Allows this component to update the reference count of any components
+ * that it references.
+ */
+ public void addSubReference() {
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/NodeState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/NodeState.java
new file mode 100644
index 0000000..c6fb735
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/NodeState.java
@@ -0,0 +1,88 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.*;
+import javax.media.j3d.Node;
+import javax.media.j3d.NodeComponent;
+import javax.media.j3d.SceneGraphObject;
+
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class NodeState extends SceneGraphObjectState {
+
+ public NodeState( SymbolTableData symbol, Controller control ) {
+ super(symbol, control);
+ }
+
+ public void writeObject( DataOutput out ) throws
+ IOException {
+
+ super.writeObject( out );
+
+ control.writeBounds( out, ((Node)node).getBounds() );
+
+ out.writeBoolean( ((Node)node).getPickable() );
+ out.writeBoolean( ((Node)node).getCollidable() );
+ out.writeBoolean( ((Node)node).getBoundsAutoCompute() );
+
+ }
+
+ public void readObject( DataInput in ) throws
+ IOException {
+ super.readObject(in);
+
+ ((Node)node).setBounds( control.readBounds(in) );
+
+ ((Node)node).setPickable( in.readBoolean() );
+ ((Node)node).setCollidable( in.readBoolean() );
+ ((Node)node).setBoundsAutoCompute( in.readBoolean() );
+ }
+
+
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/NullSceneGraphObjectState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/NullSceneGraphObjectState.java
new file mode 100644
index 0000000..17aa275
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/NullSceneGraphObjectState.java
@@ -0,0 +1,105 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.*;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import javax.media.j3d.SceneGraphObject;
+import javax.vecmath.Color3f;
+import javax.vecmath.Point3d;
+import javax.vecmath.Vector4d;
+import javax.vecmath.Tuple3d;
+import javax.vecmath.Tuple4d;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class NullSceneGraphObjectState extends SceneGraphObjectState {
+
+ SymbolTableData symbolTableData;
+
+ /**
+ * Dummy class to represent a null object in the scene graph
+ *
+ */
+ public NullSceneGraphObjectState(SymbolTableData symbol,Controller control) {
+ super( null, control );
+ symbolTableData = new SymbolTableData( -1, null, this, -1 );
+ }
+
+ /**
+ * DO NOT call symbolTable.addReference in writeObject as this (may)
+ * result in a concurrentModificationException.
+ *
+ * All references should be created in the constructor
+ */
+ public void writeObject( DataOutput out ) throws IOException {
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ }
+
+ public SceneGraphObject getNode() {
+ return null;
+ }
+
+ public int getNodeID() {
+ return -1;
+ }
+
+ public SymbolTableData getSymbol() {
+ return symbolTableData;
+ }
+
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return null;
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/OrderedGroupState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/OrderedGroupState.java
new file mode 100644
index 0000000..8a4c515
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/OrderedGroupState.java
@@ -0,0 +1,85 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import javax.media.j3d.OrderedGroup;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+
+public class OrderedGroupState extends GroupState {
+
+ /** Creates new BranchGroupState */
+ public OrderedGroupState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ int[] childIndexOrder = ((OrderedGroup)node).getChildIndexOrder();
+ out.writeInt( childIndexOrder.length );
+ for ( int i=0;i<childIndexOrder.length;i++ ) {
+ out.writeInt( childIndexOrder[ i ] );
+ }
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ int[] childIndexOrder = new int[ in.readInt() ];
+ for ( int i=0;i<childIndexOrder.length;i++ ) {
+ childIndexOrder[ i ] = in.readInt();
+ }
+ ((OrderedGroup)node).setChildIndexOrder( childIndexOrder );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new OrderedGroup();
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/OrientedShape3DState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/OrientedShape3DState.java
new file mode 100644
index 0000000..9af434b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/OrientedShape3DState.java
@@ -0,0 +1,96 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.OrientedShape3D;
+import javax.media.j3d.SceneGraphObject;
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class OrientedShape3DState extends Shape3DState {
+
+ public OrientedShape3DState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeInt( ((OrientedShape3D)node).getAlignmentMode() );
+
+ Vector3f vec = new Vector3f();
+ ((OrientedShape3D)node).getAlignmentAxis( vec );
+
+ Point3f point = new Point3f();
+ ((OrientedShape3D)node).getRotationPoint( point );
+
+ control.writeVector3f( out, vec );
+ control.writePoint3f( out, point );
+ out.writeBoolean( ((OrientedShape3D)node).getConstantScaleEnable() );
+ out.writeDouble( ((OrientedShape3D)node).getScale() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ ((OrientedShape3D)node).setAlignmentMode( in.readInt() );
+ ((OrientedShape3D)node).setAlignmentAxis( control.readVector3f( in ) );
+ ((OrientedShape3D)node).setRotationPoint( control.readPoint3f( in ) );
+ ((OrientedShape3D)node).setConstantScaleEnable( in.readBoolean() );
+ ((OrientedShape3D)node).setScale( in.readDouble() );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new OrientedShape3D();
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PathInterpolatorState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PathInterpolatorState.java
new file mode 100644
index 0000000..6e70ce0
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PathInterpolatorState.java
@@ -0,0 +1,81 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.PathInterpolator;
+import javax.media.j3d.SceneGraphObject;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public abstract class PathInterpolatorState extends TransformInterpolatorState {
+
+ protected float[] knots;
+
+ public PathInterpolatorState( SymbolTableData symbol,Controller control ) {
+ super( symbol, control );
+ }
+
+ // PathInterpolator.setKnots(float[]) is protected so we can only set the
+ // knots in the constructor
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+ knots = new float[ ((PathInterpolator)node).getArrayLengths() ];
+ out.writeInt( knots.length );
+ ((PathInterpolator)node).getKnots( knots );
+ for(int i=0; i<knots.length; i++)
+ out.writeFloat( knots[i] );
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams( in );
+ knots = new float[ in.readInt() ];
+ for(int i=0; i<knots.length; i++)
+ knots[i] = in.readFloat();
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PointAttributesState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PointAttributesState.java
new file mode 100644
index 0000000..2b99223
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PointAttributesState.java
@@ -0,0 +1,81 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.PointAttributes;
+
+public class PointAttributesState extends NodeComponentState {
+
+ public PointAttributesState( SymbolTableData symbol, Controller control ) {
+ super(symbol, control);
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ PointAttributes attr = (PointAttributes)node;
+ out.writeBoolean( attr.getPointAntialiasingEnable() );
+ out.writeFloat( attr.getPointSize() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ PointAttributes attr = (PointAttributes)node;
+ attr.setPointAntialiasingEnable( in.readBoolean() );
+ attr.setPointSize( in.readFloat() );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new PointAttributes();
+ }
+
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PointLightState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PointLightState.java
new file mode 100644
index 0000000..0a14ae9
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PointLightState.java
@@ -0,0 +1,79 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.*;
+import javax.media.j3d.PointLight;
+import javax.vecmath.Point3f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class PointLightState extends LightState {
+
+ public PointLightState( SymbolTableData symbol, Controller control ) {
+ super( symbol, control );
+
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ Point3f point = new Point3f();
+ ((PointLight)node).getAttenuation( point );
+ control.writePoint3f( out, point );
+ ((PointLight)node).getPosition( point );
+ control.writePoint3f( out, point );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ ((PointLight)node).setAttenuation( control.readPoint3f(in) );
+ ((PointLight)node).setPosition( control.readPoint3f(in) );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new PointLight();
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PointSoundState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PointSoundState.java
new file mode 100644
index 0000000..2d76886
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PointSoundState.java
@@ -0,0 +1,101 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.PointSound;
+import javax.media.j3d.SceneGraphObject;
+import javax.vecmath.Point3f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class PointSoundState extends SoundState {
+
+ public PointSoundState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ float[] distance = new float[ ((PointSound)node).getDistanceGainLength() ];
+ float[] gain = new float[ distance.length ];
+
+ ((PointSound)node).getDistanceGain( distance, gain );
+ out.writeInt( distance.length );
+ for(int i=0; i<distance.length; i++) {
+ out.writeFloat( distance[i] );
+ out.writeFloat( gain[i] );
+ }
+
+ Point3f pos = new Point3f();
+
+ ((PointSound)node).getPosition( pos );
+ control.writePoint3f( out, pos );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ float[] distance = new float[ in.readInt() ];
+ float[] gain = new float[ distance.length ];
+ for(int i=0; i<distance.length; i++) {
+ distance[i] = in.readFloat();
+ gain[i] = in.readFloat();
+ }
+ ((PointSound)node).setDistanceGain( distance, gain );
+
+ ((PointSound)node).setPosition( control.readPoint3f( in ));
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new PointSound();
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PolygonAttributesState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PolygonAttributesState.java
new file mode 100644
index 0000000..c361707
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PolygonAttributesState.java
@@ -0,0 +1,87 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.PolygonAttributes;
+
+public class PolygonAttributesState extends NodeComponentState {
+
+ public PolygonAttributesState( SymbolTableData symbol, Controller control ) {
+ super(symbol, control);
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ PolygonAttributes polyAttr = (PolygonAttributes)node;
+ out.writeBoolean( polyAttr.getBackFaceNormalFlip() );
+ out.writeInt( polyAttr.getCullFace() );
+ out.writeInt( polyAttr.getPolygonMode() );
+ out.writeFloat( polyAttr.getPolygonOffset() );
+ out.writeFloat( polyAttr.getPolygonOffsetFactor() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ PolygonAttributes polyAttr = (PolygonAttributes)node;
+ polyAttr.setBackFaceNormalFlip( in.readBoolean() );
+ polyAttr.setCullFace( in.readInt() );
+ polyAttr.setPolygonMode( in.readInt() );
+ polyAttr.setPolygonOffset( in.readFloat() );
+ polyAttr.setPolygonOffsetFactor( in.readFloat() );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new PolygonAttributes();
+ }
+
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PositionInterpolatorState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PositionInterpolatorState.java
new file mode 100644
index 0000000..0e1a2d3
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PositionInterpolatorState.java
@@ -0,0 +1,88 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.PositionInterpolator;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.TransformGroup;
+import javax.vecmath.Point3f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class PositionInterpolatorState extends TransformInterpolatorState {
+
+ public PositionInterpolatorState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeFloat( ((PositionInterpolator)node).getStartPosition() );
+ out.writeFloat( ((PositionInterpolator)node).getEndPosition() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ ((PositionInterpolator)node).setStartPosition( in.readFloat() );
+ ((PositionInterpolator)node).setEndPosition( in.readFloat() );
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] { javax.media.j3d.Alpha.class,
+ TransformGroup.class },
+ new Object[] { null,
+ null } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new PositionInterpolator( null, null );
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PositionPathInterpolatorState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PositionPathInterpolatorState.java
new file mode 100644
index 0000000..7d47bee
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/PositionPathInterpolatorState.java
@@ -0,0 +1,104 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.PositionPathInterpolator;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.TransformGroup;
+import javax.vecmath.Point3f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class PositionPathInterpolatorState extends PathInterpolatorState {
+
+ private Point3f[] positions;
+
+ public PositionPathInterpolatorState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+
+ positions = new Point3f[ knots.length ];
+ for(int i=0; i<positions.length; i++)
+ positions[i] = new Point3f();
+
+ ((PositionPathInterpolator)node).getPositions( positions );
+ for(int i=0; i<positions.length; i++)
+ control.writePoint3f( out, positions[i] );
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams( in );
+
+ positions = new Point3f[ knots.length ];
+ for(int i=0; i<positions.length; i++)
+ positions[i] = control.readPoint3f( in );
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] { javax.media.j3d.Alpha.class,
+ TransformGroup.class,
+ Transform3D.class,
+ knots.getClass(),
+ positions.getClass() },
+ new Object[] { null,
+ null,
+ null,
+ knots,
+ positions } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new PositionPathInterpolator( null, null, null, knots, positions );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/QuadArrayState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/QuadArrayState.java
new file mode 100644
index 0000000..3e8a648
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/QuadArrayState.java
@@ -0,0 +1,86 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import javax.media.j3d.QuadArray;
+import javax.media.j3d.SceneGraphObject;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class QuadArrayState extends GeometryArrayState {
+
+ public QuadArrayState( SymbolTableData symbol, Controller control ) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ }
+
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return super.createNode( j3dClass, new Class[] { Integer.TYPE,
+ Integer.TYPE },
+ new Object[] { new Integer( vertexCount ),
+ new Integer( vertexFormat ) });
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new QuadArray( vertexCount, vertexFormat );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RasterState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RasterState.java
new file mode 100644
index 0000000..801c937
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RasterState.java
@@ -0,0 +1,139 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.Raster;
+import javax.media.j3d.SceneGraphObject;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import java.awt.Point;
+import javax.vecmath.Point3f;
+import java.awt.Dimension;
+import javax.media.j3d.ImageComponent2D;
+import javax.media.j3d.DepthComponent;
+
+public class RasterState extends GeometryState {
+
+ int image;
+ int depthComponent;
+
+ public RasterState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+
+ // Set up references during save
+ if ( node!=null ) {
+ image = control.getSymbolTable().addReference( ((Raster)node).getImage() );
+ depthComponent =
+ control.getSymbolTable().addReference( ((Raster)node).getDepthComponent() );
+ }
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeInt( image );
+ out.writeInt( depthComponent );
+
+ Point3f pos = new Point3f();
+ ((Raster)node).getPosition( pos );
+ control.writePoint3f( out, pos );
+
+ out.writeInt( ((Raster)node).getType() );
+ out.writeInt( ((Raster)node).getClipMode() );
+
+ Point offset = new Point();
+ ((Raster)node).getSrcOffset( offset );
+ out.writeInt( offset.x );
+ out.writeInt( offset.y );
+
+ Dimension size = new Dimension();
+ ((Raster)node).getSize( size );
+ out.writeInt( size.width );
+ out.writeInt( size.height );
+
+ ((Raster)node).getDstOffset( offset );
+ out.writeInt( offset.x );
+ out.writeInt( offset.y );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ image = in.readInt();
+ depthComponent = in.readInt();
+
+ ((Raster)node).setPosition( control.readPoint3f( in ) );
+ ((Raster)node).setType( in.readInt() );
+ ((Raster)node).setClipMode( in.readInt() );
+ ((Raster)node).setSrcOffset( new Point( in.readInt(), in.readInt() ) );
+ ((Raster)node).setSize( new Dimension( in.readInt(), in.readInt() ) );
+ ((Raster)node).setDstOffset( new Point( in.readInt(), in.readInt() ) );
+ }
+
+ /**
+ * Called when this component reference count is incremented.
+ * Allows this component to update the reference count of any components
+ * that it references.
+ */
+ public void addSubReference() {
+ control.getSymbolTable().incNodeComponentRefCount( image );
+ control.getSymbolTable().incNodeComponentRefCount( depthComponent );
+ }
+
+ // Set up references during load
+ public void buildGraph() {
+ ((Raster)node).setImage( (ImageComponent2D)control.getSymbolTable().getJ3dNode( image ) );
+ ((Raster)node).setDepthComponent(
+ (DepthComponent)control.getSymbolTable().getJ3dNode( depthComponent ) );
+ super.buildGraph(); // Must be last call in method
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new Raster();
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RenderingAttributesState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RenderingAttributesState.java
new file mode 100644
index 0000000..7c98d65
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RenderingAttributesState.java
@@ -0,0 +1,93 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.RenderingAttributes;
+
+public class RenderingAttributesState extends NodeComponentState {
+
+ public RenderingAttributesState( SymbolTableData symbol, Controller control ) {
+ super(symbol, control);
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ RenderingAttributes attr = (RenderingAttributes)node;
+ out.writeInt( attr.getAlphaTestFunction() );
+ out.writeFloat( attr.getAlphaTestValue() );
+ out.writeBoolean( attr.getDepthBufferEnable() );
+ out.writeBoolean( attr.getDepthBufferWriteEnable() );
+ out.writeBoolean( attr.getIgnoreVertexColors() );
+ out.writeInt( attr.getRasterOp() );
+ out.writeBoolean( attr.getRasterOpEnable() );
+ out.writeBoolean( attr.getVisible() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ RenderingAttributes attr = (RenderingAttributes)node;
+ attr.setAlphaTestFunction( in.readInt() );
+ attr.setAlphaTestValue( in.readFloat() );
+ attr.setDepthBufferEnable( in.readBoolean() );
+ attr.setDepthBufferWriteEnable( in.readBoolean() );
+ attr.setIgnoreVertexColors( in.readBoolean() );
+ attr.setRasterOp( in.readInt() );
+ attr.setRasterOpEnable( in.readBoolean() );
+ attr.setVisible( in.readBoolean() );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new RenderingAttributes();
+ }
+
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RotPosPathInterpolatorState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RotPosPathInterpolatorState.java
new file mode 100644
index 0000000..65a67c4
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RotPosPathInterpolatorState.java
@@ -0,0 +1,118 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.RotPosPathInterpolator;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.TransformGroup;
+import javax.vecmath.Point3f;
+import javax.vecmath.Quat4f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class RotPosPathInterpolatorState extends PathInterpolatorState {
+
+ private Point3f[] positions;
+ private Quat4f[] quats;
+
+ public RotPosPathInterpolatorState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+
+ positions = new Point3f[ knots.length ];
+ quats = new Quat4f[ knots.length ];
+ for(int i=0; i<positions.length; i++) {
+ positions[i] = new Point3f();
+ quats[i] = new Quat4f();
+ }
+
+ ((RotPosPathInterpolator)node).getPositions( positions );
+ ((RotPosPathInterpolator)node).getQuats( quats );
+ for(int i=0; i<positions.length; i++) {
+ control.writePoint3f( out, positions[i] );
+ control.writeQuat4f( out, quats[i] );
+ }
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams( in );
+
+ positions = new Point3f[ knots.length ];
+ quats = new Quat4f[ knots.length ];
+ for(int i=0; i<positions.length; i++) {
+ positions[i] = control.readPoint3f( in );
+ quats[i] = control.readQuat4f( in );
+ }
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] { javax.media.j3d.Alpha.class,
+ TransformGroup.class,
+ Transform3D.class,
+ knots.getClass(),
+ quats.getClass(),
+ positions.getClass() },
+ new Object[] { null,
+ null,
+ null,
+ knots,
+ quats,
+ positions } );
+
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new RotPosPathInterpolator( null, null, null, knots, quats, positions );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RotPosScalePathInterpolatorState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RotPosScalePathInterpolatorState.java
new file mode 100644
index 0000000..895c02f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RotPosScalePathInterpolatorState.java
@@ -0,0 +1,124 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.RotPosScalePathInterpolator;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.TransformGroup;
+import javax.vecmath.Point3f;
+import javax.vecmath.Quat4f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class RotPosScalePathInterpolatorState extends PathInterpolatorState {
+
+ private Point3f[] positions;
+ private Quat4f[] quats;
+ private float[] scales;
+
+ public RotPosScalePathInterpolatorState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+
+ positions = new Point3f[ knots.length ];
+ quats = new Quat4f[ knots.length ];
+ scales = new float[ knots.length ];
+ for(int i=0; i<positions.length; i++) {
+ positions[i] = new Point3f();
+ quats[i] = new Quat4f();
+ }
+
+ ((RotPosScalePathInterpolator)node).getPositions( positions );
+ ((RotPosScalePathInterpolator)node).getQuats( quats );
+ ((RotPosScalePathInterpolator)node).getScales( scales );
+ for(int i=0; i<positions.length; i++) {
+ control.writePoint3f( out, positions[i] );
+ control.writeQuat4f( out, quats[i] );
+ out.writeFloat( scales[i] );
+ }
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams( in );
+
+ positions = new Point3f[ knots.length ];
+ quats = new Quat4f[ knots.length ];
+ scales = new float[ knots.length ];
+ for(int i=0; i<positions.length; i++) {
+ positions[i] = control.readPoint3f( in );
+ quats[i] = control.readQuat4f( in );
+ scales[i] = in.readFloat();
+ }
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] { javax.media.j3d.Alpha.class,
+ TransformGroup.class,
+ Transform3D.class,
+ knots.getClass(),
+ quats.getClass(),
+ positions.getClass(),
+ scales.getClass() },
+ new Object[] { null,
+ null,
+ null,
+ knots,
+ quats,
+ positions,
+ scales } );
+
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new RotPosScalePathInterpolator( null, null, null, knots, quats, positions, scales );
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RotationInterpolatorState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RotationInterpolatorState.java
new file mode 100644
index 0000000..67f72be
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RotationInterpolatorState.java
@@ -0,0 +1,90 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.RotationInterpolator;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.TransformGroup;
+import javax.media.j3d.Alpha;
+import javax.vecmath.Matrix4d;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class RotationInterpolatorState extends TransformInterpolatorState {
+
+ public RotationInterpolatorState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ RotationInterpolator interp = (RotationInterpolator)node;
+ out.writeFloat( interp.getMinimumAngle() );
+ out.writeFloat( interp.getMaximumAngle() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ RotationInterpolator interp = (RotationInterpolator)node;
+ interp.setMinimumAngle( in.readFloat() );
+ interp.setMaximumAngle( in.readFloat() );
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] { Alpha.class, TransformGroup.class },
+ new Object[] { null, null } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new RotationInterpolator( null, null );
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RotationPathInterpolatorState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RotationPathInterpolatorState.java
new file mode 100644
index 0000000..954063f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/RotationPathInterpolatorState.java
@@ -0,0 +1,108 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.RotationPathInterpolator;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.TransformGroup;
+import javax.vecmath.Quat4f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class RotationPathInterpolatorState extends PathInterpolatorState {
+
+ private Quat4f[] quats;
+
+ public RotationPathInterpolatorState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+
+ quats = new Quat4f[ knots.length ];
+ for(int i=0; i<quats.length; i++) {
+ quats[i] = new Quat4f();
+ }
+
+ ((RotationPathInterpolator)node).getQuats( quats );
+ for(int i=0; i<quats.length; i++) {
+ control.writeQuat4f( out, quats[i] );
+ }
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams( in );
+
+ quats = new Quat4f[ knots.length ];
+ for(int i=0; i<quats.length; i++) {
+ quats[i] = control.readQuat4f( in );
+ }
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] { javax.media.j3d.Alpha.class,
+ TransformGroup.class,
+ Transform3D.class,
+ knots.getClass(),
+ quats.getClass() },
+ new Object[] { null,
+ null,
+ null,
+ knots,
+ quats } );
+
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new RotationPathInterpolator( null, null, null, knots, quats );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ScaleInterpolatorState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ScaleInterpolatorState.java
new file mode 100644
index 0000000..5616c61
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ScaleInterpolatorState.java
@@ -0,0 +1,89 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.ScaleInterpolator;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.TransformGroup;
+import javax.vecmath.Point3f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class ScaleInterpolatorState extends TransformInterpolatorState {
+
+ public ScaleInterpolatorState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeFloat( ((ScaleInterpolator)node).getMinimumScale() );
+ out.writeFloat( ((ScaleInterpolator)node).getMaximumScale() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ ((ScaleInterpolator)node).setMinimumScale( in.readFloat() );
+ ((ScaleInterpolator)node).setMaximumScale( in.readFloat() );
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] { javax.media.j3d.Alpha.class,
+ TransformGroup.class },
+ new Object[] { null,
+ null } );
+
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new ScaleInterpolator( null, null );
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SceneGraphObjectState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SceneGraphObjectState.java
new file mode 100644
index 0000000..0dc864d
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SceneGraphObjectState.java
@@ -0,0 +1,506 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.*;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import javax.media.j3d.SceneGraphObject;
+import javax.vecmath.Color3f;
+import javax.vecmath.Point3d;
+import javax.vecmath.Vector4d;
+import javax.vecmath.Tuple3d;
+import javax.vecmath.Tuple4d;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import com.sun.j3d.utils.scenegraph.io.retained.SGIORuntimeException;
+
+public abstract class SceneGraphObjectState {
+
+ protected SceneGraphObject node;
+ protected SymbolTableData symbol;
+ protected Controller control;
+ protected String nodeClassName;
+
+ /**
+ * Create a new State object
+ *
+ * During Saveing
+ * SymbolTableData will have the nodeID and j3dNode fields set
+ *
+ * During loading
+ * SymbolTableData be null, symbol will be created and added to the
+ * symbol data during readObject()
+ */
+ public SceneGraphObjectState( SymbolTableData symbol, Controller control ) {
+ this.symbol = symbol;
+ this.control = control;
+
+ if (symbol!=null) {
+ this.node = symbol.j3dNode;
+
+ // This consistancy check is for debugging purposes
+ //if (symbol.j3dNode==null || symbol.nodeID==0)
+ // throw new RuntimeException( "Bad Symbol in State creation");
+ }
+
+
+ if (node!=null) {
+ nodeClassName = node.getClass().getName();
+
+ try {
+ if (node instanceof com.sun.j3d.utils.scenegraph.io.SceneGraphIO)
+ ((com.sun.j3d.utils.scenegraph.io.SceneGraphIO)node).createSceneGraphObjectReferences( control.getSymbolTable() );
+ } catch(Exception e) {
+ System.err.println("Exception in createSceneGraphObjectReferences");
+ e.printStackTrace();
+ }
+
+ }
+
+ }
+
+ /**
+ * DO NOT call symbolTable.addReference in writeObject as this (may)
+ * result in a concurrentModificationException.
+ *
+ * All references should be created in the constructor
+ */
+ public void writeObject( DataOutput out ) throws IOException {
+ boolean sgIO = node instanceof com.sun.j3d.utils.scenegraph.io.SceneGraphIO;
+ out.writeBoolean( sgIO );
+ out.writeInt( symbol.nodeID );
+
+
+ int nodeClassID = control.getNodeClassID( node );
+
+ out.writeShort( nodeClassID );
+
+ if (nodeClassID==-1)
+ out.writeUTF( nodeClassName );
+
+ writeConstructorParams( out );
+
+ if (sgIO) {
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+ DataOutputStream tmpOut = new DataOutputStream( byteStream );
+ ((com.sun.j3d.utils.scenegraph.io.SceneGraphIO)node).writeSceneGraphObject( tmpOut );
+ tmpOut.close();
+ out.writeInt( byteStream.size() );
+ out.write( byteStream.toByteArray() );
+ }
+
+ writeUserData( out );
+
+ writeCapabilities( out );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+
+ boolean sgIO = in.readBoolean();
+ int nodeID = in.readInt();
+
+ int nodeClassID = in.readShort();
+
+ nodeClassName = null;
+
+ if ( nodeClassID==-1 )
+ nodeClassName = in.readUTF();
+
+ readConstructorParams( in );
+
+ if ( nodeClassID!=-1 ) {
+ node = createNode();
+ nodeClassName = node.getClass().getName();
+ } else
+ node = createNode(nodeClassName);
+
+
+ if ( sgIO ) {
+ if (control.getCurrentFileVersion()==1)
+ ((com.sun.j3d.utils.scenegraph.io.SceneGraphIO)node).readSceneGraphObject( in );
+ else {
+ int size = in.readInt();
+ if (node instanceof com.sun.j3d.utils.scenegraph.io.SceneGraphIO) {
+ byte[] bytes = new byte[size];
+ in.readFully( bytes );
+ ByteArrayInputStream byteStream = new ByteArrayInputStream( bytes );
+ DataInputStream tmpIn = new DataInputStream( byteStream );
+ ((com.sun.j3d.utils.scenegraph.io.SceneGraphIO)node).readSceneGraphObject( tmpIn );
+ tmpIn.close();
+ } else {
+ in.skipBytes( size );
+ }
+ }
+ }
+
+ symbol = control.getSymbolTable().createSymbol( this, node, nodeID );
+ readUserData( in );
+
+ readCapabilities( in );
+ }
+
+ public SceneGraphObject getNode() {
+ return node;
+ }
+
+ public int getNodeID() {
+ return symbol.nodeID;
+ }
+
+ public SymbolTableData getSymbol() {
+ return symbol;
+ }
+
+ private void readUserData( DataInput in ) throws IOException {
+
+ node.setUserData( control.readSerializedData( in ));
+ }
+
+ private void writeUserData( DataOutput out ) throws IOException {
+ Object obj = node.getUserData();
+ if (obj != null && !(obj instanceof java.io.Serializable)) {
+ System.err.println("UserData is not Serializable and will not be saved");
+ obj = null;
+ }
+
+ control.writeSerializedData( out, (Serializable)obj );
+ }
+
+ /*
+ * NOTE: This implementation assumes a maximum of 64 capability
+ * bits per node class. If this changes in the future, this
+ * implementation will need to be updated.
+ */
+ private void writeCapabilities( DataOutput out ) throws IOException {
+ long capabilities = 0;
+ long frequentCapabilities = 0;
+
+ for ( int i=0;i<64;i++ ) {
+ if ( node.getCapability( i ) ) capabilities |= (1L << i);
+ if ( !(node.getCapabilityIsFrequent( i )) ) frequentCapabilities |= (1L << i);
+ }
+ out.writeLong( capabilities );
+ out.writeLong( frequentCapabilities );
+ }
+
+ private void readCapabilities( DataInput in ) throws IOException {
+ long capabilities = in.readLong();
+ long frequentCapabilities = in.readLong();
+
+ for ( int i=0;i<64;i++ ) {
+ if ( (capabilities&(1L<<i))!=0L ) node.setCapability( i );
+ if ( (frequentCapabilities&(1L<<i))!=0L ) node.clearCapabilityIsFrequent( i );
+ }
+ }
+
+ /**
+ * Write the parameters required for the constructor of the Java3D object
+ */
+ protected void writeConstructorParams( DataOutput out ) throws
+ IOException {
+ }
+
+ /**
+ * Read the parameters required for the constructor of the Java3D object
+ */
+ protected void readConstructorParams( DataInput in ) throws
+ IOException {
+ }
+
+ /**
+ * Create a new Java3D node for this object.
+ *
+ * This method is ONLY used when the Java3D Class type matches the
+ * State type, ie this does NOT handle subclasses of Java3D.
+ *
+ * For Java3D subclasses use createNode( Class state)
+ *
+ * This method MUST be implemented by all State objects but is not
+ * abstract to allow for external subclassing
+ */
+ protected SceneGraphObject createNode() {
+ throw new SGIORuntimeException("createNode() not implemented in class "+this.getClass().getName());
+ }
+
+ /**
+ * Create a new Java3D node from the supplied class using the parameterless constructor
+ *
+ * For Java3D nodes which do not have a default constructor you must
+ * overload this method and create the object using createNode( className, parameters )
+ * This will correctly handle subclasses of Java3D classes
+ */
+ protected SceneGraphObject createNode( Class state ) {
+ SceneGraphObject ret;
+
+ try {
+ ret = (SceneGraphObject)state.newInstance();
+
+ //System.err.println("Created J3D node for "+className );
+ } catch( IllegalAccessException exce ) {
+ throw new SGIORuntimeException( "Broken State class for "+
+ state.getClass().getName()+" - IllegalAccess" );
+ } catch( InstantiationException excep ) {
+ throw new SGIORuntimeException( "Broken State class for "+
+ state.getClass().getName() );
+ }
+
+ return ret;
+ }
+
+ /**
+ * Create a new Java3D node from the supplied class name using the parameterless constructor
+ *
+ * For Java3D nodes which do not have a default constructor you must
+ * overload this method and create the object using createNode( className, parameters )
+ * This will correctly handle subclasses of Java3D classes
+ */
+ protected SceneGraphObject createNode( String className ) {
+ SceneGraphObject ret;
+
+ try {
+ Class state = Class.forName( className, true, control.getClassLoader() );
+
+ ret = createNode( state );
+
+ //System.err.println("Created J3D node for "+className );
+ } catch(ClassNotFoundException e) {
+ if (control.useSuperClassIfNoChildClass())
+ ret = createNodeFromSuper( className );
+ else
+ throw new SGIORuntimeException( "No Such Class "+
+ className );
+ }
+
+ return ret;
+ }
+
+ /**
+ * If createNode cannot locate the correct class to instantiate
+ * the node this method is called and will instantiate the
+ * node using it's Java3D Core superclass
+ */
+ private SceneGraphObject createNodeFromSuper( String className ) {
+ SceneGraphObject ret;
+
+ String tmp = this.getClass().getName();
+ String superClass = tmp.substring( tmp.indexOf("state")+6, tmp.length()-5 );
+
+ System.err.println("Unable to create node "+className+" attempting Java3D superclass "+superClass );
+
+ try {
+ Class state = Class.forName( superClass, true, control.getClassLoader() );
+
+ ret = (SceneGraphObject)state.newInstance();
+
+ } catch(ClassNotFoundException e) {
+ throw new SGIORuntimeException( "No Such Class "+
+ className );
+ } catch( IllegalAccessException exce ) {
+ throw new SGIORuntimeException( "Broken State class for "+
+ className+" - IllegalAccess" );
+ } catch( InstantiationException excep ) {
+ throw new SGIORuntimeException( "Unable to instantiate class "+
+ className );
+ }
+
+ return ret;
+ }
+
+ /**
+ * Create a Java3D node which does not have a default constructor
+ *
+ * parameterTypes must contain the classes required by the constructor,
+ * use Integer.TYPE, Float.TYPE etc to specifiy primitive types
+ *
+ * paramters should contain the list of parameters for the constructor,
+ * primitive types should be wrapped in the appropriate class (ie Integer, Float )
+ */
+ private SceneGraphObject createNode( String className, Class[] parameterTypes, Object[] parameters ) {
+ SceneGraphObject ret;
+ Constructor constructor;
+
+ try {
+ Class state = Class.forName( className );
+ constructor = state.getConstructor( parameterTypes );
+ ret = (SceneGraphObject)constructor.newInstance( parameters );
+ } catch(ClassNotFoundException e1) {
+ if (control.useSuperClassIfNoChildClass())
+ ret = createNodeFromSuper( className, parameterTypes, parameters );
+ else
+ throw new SGIORuntimeException( "No State class for "+
+ className );
+ } catch( IllegalAccessException e2 ) {
+ throw new SGIORuntimeException( "Broken State class for "+
+ className+" - IllegalAccess" );
+ } catch( InstantiationException e3 ) {
+ throw new SGIORuntimeException( "Broken State class for "+
+ className );
+ } catch( java.lang.reflect.InvocationTargetException e4 ) {
+ throw new SGIORuntimeException( "InvocationTargetException for "+
+ className );
+ } catch( NoSuchMethodException e5 ) {
+ for(int i=0; i<parameterTypes.length; i++)
+ System.err.println( parameterTypes[i].getName() );
+ System.err.println("------");
+ throw new SGIORuntimeException( "Invalid constructor for "+
+ className );
+ }
+
+ return ret;
+ }
+
+ /**
+ * Create a Java3D node which does not have a default constructor
+ *
+ * parameterTypes must contain the classes required by the constructor,
+ * use Interger.TYPE, Float.TYPE etc to specifiy primitive types
+ *
+ * paramters should contain the list of parameters for the constructor,
+ * primitive types should be wrapped in the appropriate class (ie Integer, Float )
+ */
+ protected SceneGraphObject createNode( Class j3dClass, Class[] parameterTypes, Object[] parameters ) {
+ SceneGraphObject ret;
+ Constructor constructor;
+
+ try {
+ constructor = j3dClass.getConstructor( parameterTypes );
+ ret = (SceneGraphObject)constructor.newInstance( parameters );
+ } catch( IllegalAccessException e2 ) {
+ throw new SGIORuntimeException( "Broken State class for "+
+ j3dClass.getClass().getName()+" - IllegalAccess" );
+ } catch( InstantiationException e3 ) {
+ throw new SGIORuntimeException( "Broken State class for "+
+ j3dClass.getClass().getName() );
+ } catch( java.lang.reflect.InvocationTargetException e4 ) {
+ throw new SGIORuntimeException( "InvocationTargetException for "+
+ j3dClass.getClass().getName() );
+ } catch( NoSuchMethodException e5 ) {
+ for(int i=0; i<parameterTypes.length; i++)
+ System.err.println( parameterTypes[i].getName() );
+ System.err.println("------");
+ throw new SGIORuntimeException( "Invalid constructor for "+
+ j3dClass.getClass().getName() );
+ }
+
+ return ret;
+ }
+
+ /**
+ * If createNode cannot locate the correct class to instantiate
+ * the node this method is called and will instantiate the
+ * node using it's Java3D Core superclass
+ */
+ private SceneGraphObject createNodeFromSuper( String className, Class[] parameterTypes, Object[] parameters ) {
+ SceneGraphObject ret;
+
+ String tmp = this.getClass().getName();
+ String superClass = tmp.substring( tmp.indexOf("state")+6, tmp.length()-5 );
+ Constructor constructor;
+
+ try {
+ Class state = Class.forName( superClass );
+ constructor = state.getConstructor( parameterTypes );
+ ret = (SceneGraphObject)constructor.newInstance( parameters );
+ } catch(ClassNotFoundException e1) {
+ throw new SGIORuntimeException( "No State class for "+
+ superClass );
+ } catch( IllegalAccessException e2 ) {
+ throw new SGIORuntimeException( "Broken State class for "+
+ className+" - IllegalAccess" );
+ } catch( InstantiationException e3 ) {
+ throw new SGIORuntimeException( "Broken State class for "+
+ className );
+ } catch( java.lang.reflect.InvocationTargetException e4 ) {
+ throw new SGIORuntimeException( "InvocationTargetException for "+
+ className );
+ } catch( NoSuchMethodException e5 ) {
+ for(int i=0; i<parameterTypes.length; i++)
+ System.err.println( parameterTypes[i].getName() );
+ System.err.println("------");
+ throw new SGIORuntimeException( "Invalid constructor for "+
+ className );
+ }
+
+ return ret;
+ }
+
+ /**
+ * Given a scene graph object instantiate the correct State class
+ * for that object
+ */
+ protected SceneGraphObjectState createState( SceneGraphObject obj, Controller control ) {
+
+ return control.createState( obj );
+ }
+
+ /**
+ * Return the class name of the Class, the fully qualified classname
+ * is stripped of all package information and returned
+ */
+ private String getClassName( Class c ) {
+ return c.getName().substring(c.getName().lastIndexOf('.')+1);
+ }
+
+ /**
+ * Subclasses should processes their own buildGraph requirements BEFORE
+ * calling super.buildGraph().
+ *
+ * This ensures that when restoreSceneGraphObjectReferences is called in
+ * user code our references have been resolved
+ */
+ public void buildGraph() {
+ //System.err.println("Build Graph "+this);
+ if (node instanceof com.sun.j3d.utils.scenegraph.io.SceneGraphIO)
+ ((com.sun.j3d.utils.scenegraph.io.SceneGraphIO)node).restoreSceneGraphObjectReferences( control.getSymbolTable() );
+ }
+
+ public void cleanup() {
+ control = null;
+ node = null;
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/Shape3DState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/Shape3DState.java
new file mode 100644
index 0000000..9a17327
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/Shape3DState.java
@@ -0,0 +1,121 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.Shape3D;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Geometry;
+import javax.media.j3d.Appearance;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class Shape3DState extends LeafState {
+
+ private int[] geometry;
+ private int appearance;
+
+ public Shape3DState( SymbolTableData symbol, Controller control ) {
+ super( symbol, control );
+
+ if (node!=null) {
+ appearance = control.getSymbolTable().addReference( ((Shape3D)node).getAppearance() );
+ int length = ((Shape3D)node).numGeometries();
+ geometry = new int[length];
+ for(int i=0; i<length; i++)
+ geometry[i] = control.getSymbolTable().addReference( ((Shape3D)node).getGeometry(i) );
+ }
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ control.writeBounds( out, ((Shape3D)node).getCollisionBounds() );
+
+ out.writeInt( appearance );
+
+ out.writeInt( geometry.length );
+ for(int i=0; i<geometry.length; i++)
+ out.writeInt( geometry[i] );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ ((Shape3D)node).setCollisionBounds( control.readBounds( in ));
+ appearance = in.readInt();
+
+ geometry = new int[ in.readInt() ];
+ for(int i=0; i<geometry.length; i++) {
+ geometry[i] = in.readInt();
+ }
+ }
+
+ /**
+ * Called when this component reference count is incremented.
+ * Allows this component to update the reference count of any components
+ * that it references.
+ */
+ public void addSubReference() {
+ control.getSymbolTable().incNodeComponentRefCount( appearance );
+ }
+
+ public void buildGraph() {
+ ((Shape3D)node).setAppearance( (Appearance)control.getSymbolTable().getJ3dNode( appearance ) );
+
+ ((Shape3D)node).setGeometry( (Geometry)control.getSymbolTable().getJ3dNode( geometry[0] ) );
+ for(int i=1; i<geometry.length; i++) {
+ ((Shape3D)node).addGeometry( (Geometry)control.getSymbolTable().getJ3dNode( geometry[i] ) );
+ }
+ super.buildGraph(); // Must be last call in method
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new Shape3D();
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SharedGroupState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SharedGroupState.java
new file mode 100644
index 0000000..0b8e7e2
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SharedGroupState.java
@@ -0,0 +1,63 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import javax.media.j3d.SharedGroup;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class SharedGroupState extends GroupState {
+
+ /** Creates new BranchGroupState */
+ public SharedGroupState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new SharedGroup();
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SoundState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SoundState.java
new file mode 100644
index 0000000..ff9c921
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SoundState.java
@@ -0,0 +1,127 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.Sound;
+import javax.media.j3d.MediaContainer;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.BoundingLeaf;
+import javax.media.j3d.SceneGraphObject;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public abstract class SoundState extends LeafState {
+
+ private int boundingLeaf;
+ private int mediaContainer;
+
+ public SoundState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+
+ if (node!=null) {
+ boundingLeaf = control.getSymbolTable().addReference( ((Sound)node).getSchedulingBoundingLeaf() );
+ mediaContainer = control.getSymbolTable().addReference( ((Sound)node).getSoundData() );
+ }
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ Sound sound = (Sound)node;
+
+ out.writeBoolean( sound.getContinuousEnable() );
+ out.writeBoolean( sound.getEnable() );
+ out.writeFloat( sound.getInitialGain() );
+ out.writeInt( sound.getLoop() );
+ out.writeFloat( sound.getPriority() );
+ out.writeBoolean( sound.getReleaseEnable() );
+ out.writeInt( boundingLeaf );
+ control.writeBounds( out, sound.getSchedulingBounds() );
+ out.writeInt( mediaContainer );
+ out.writeBoolean( sound.getMute() );
+ out.writeBoolean( sound.getPause() );
+ out.writeFloat( sound.getRateScaleFactor() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ Sound sound = (Sound)node;
+
+ sound.setContinuousEnable( in.readBoolean() );
+ sound.setEnable( in.readBoolean() );
+ sound.setInitialGain( in.readFloat() );
+ sound.setLoop( in.readInt() );
+ sound.setPriority( in.readFloat() );
+ sound.setReleaseEnable( in.readBoolean() );
+ boundingLeaf = in.readInt();
+ sound.setSchedulingBounds( control.readBounds( in ));
+ mediaContainer = in.readInt();
+ sound.setMute( in.readBoolean() );
+ sound.setPause( in.readBoolean() );
+ sound.setRateScaleFactor( in.readFloat() );
+ }
+
+ /**
+ * Called when this component reference count is incremented.
+ * Allows this component to update the reference count of any components
+ * that it references.
+ */
+ public void addSubReference() {
+ control.getSymbolTable().incNodeComponentRefCount( mediaContainer );
+ }
+
+ public void buildGraph() {
+
+ ((Sound)node).setSchedulingBoundingLeaf( (BoundingLeaf)control.getSymbolTable().getJ3dNode( boundingLeaf ));
+ ((Sound)node).setSoundData( (MediaContainer)control.getSymbolTable().getJ3dNode( mediaContainer ));
+ super.buildGraph(); // Must be last call in method
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SoundscapeState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SoundscapeState.java
new file mode 100644
index 0000000..0fc36af
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SoundscapeState.java
@@ -0,0 +1,103 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.Soundscape;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.BoundingLeaf;
+import javax.media.j3d.AuralAttributes;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class SoundscapeState extends LeafState {
+
+ private int boundingLeaf;
+ private int auralAttributes;
+
+ public SoundscapeState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeInt( control.getSymbolTable().addReference( ((Soundscape)node).getApplicationBoundingLeaf() ));
+ control.writeBounds( out, ((Soundscape)node).getApplicationBounds() );
+ out.writeInt( control.getSymbolTable().addReference( ((Soundscape)node).getAuralAttributes() ));
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ boundingLeaf = in.readInt();
+ ((Soundscape)node).setApplicationBounds( control.readBounds(in));
+ auralAttributes = in.readInt();
+ }
+
+ /**
+ * Called when this component reference count is incremented.
+ * Allows this component to update the reference count of any components
+ * that it references.
+ */
+ public void addSubReference() {
+ control.getSymbolTable().incNodeComponentRefCount( auralAttributes );
+ }
+
+ public void buildGraph() {
+ ((Soundscape)node).setApplicationBoundingLeaf( (BoundingLeaf)control.getSymbolTable().getJ3dNode( boundingLeaf ));
+ ((Soundscape)node).setAuralAttributes( (AuralAttributes)control.getSymbolTable().getJ3dNode( auralAttributes ));
+ super.buildGraph(); // Must be last call in method
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new Soundscape();
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SpotLightState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SpotLightState.java
new file mode 100644
index 0000000..a36cb6f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SpotLightState.java
@@ -0,0 +1,81 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.*;
+import javax.media.j3d.SpotLight;
+import javax.vecmath.Vector3f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class SpotLightState extends LightState {
+
+ public SpotLightState( SymbolTableData symbol, Controller control ) {
+ super( symbol, control );
+
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ Vector3f dir = new Vector3f();
+ ((SpotLight)node).getDirection( dir );
+ control.writeVector3f( out, dir );
+
+ out.writeFloat( ((SpotLight)node).getSpreadAngle());
+ out.writeFloat( ((SpotLight)node).getConcentration());
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ ((SpotLight)node).setDirection( control.readVector3f(in) );
+ ((SpotLight)node).setSpreadAngle( in.readFloat() );
+ ((SpotLight)node).setConcentration( in.readFloat() );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new SpotLight();
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SwitchState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SwitchState.java
new file mode 100644
index 0000000..f1f7eb2
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SwitchState.java
@@ -0,0 +1,82 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Switch;
+
+public class SwitchState extends GroupState {
+
+ public SwitchState(SymbolTableData symbol,Controller control) {
+ super(symbol, control);
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ Switch attr = (Switch)node;
+ control.writeSerializedData( out, attr.getChildMask() );
+ out.writeInt( attr.getWhichChild() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ Switch attr = (Switch)node;
+
+ attr.setChildMask( (java.util.BitSet)control.readSerializedData(in) );
+ attr.setWhichChild( in.readInt() );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new Switch();
+ }
+
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SwitchValueInterpolatorState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SwitchValueInterpolatorState.java
new file mode 100644
index 0000000..471bcd7
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/SwitchValueInterpolatorState.java
@@ -0,0 +1,104 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.SwitchValueInterpolator;
+import javax.media.j3d.Switch;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.TransformGroup;
+import javax.vecmath.Point3f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class SwitchValueInterpolatorState extends InterpolatorState {
+
+ private int target;
+
+ public SwitchValueInterpolatorState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+
+ if (node!=null)
+ target = control.getSymbolTable().addReference( ((SwitchValueInterpolator)node).getTarget() );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeInt( target );
+ out.writeInt( ((SwitchValueInterpolator)node).getFirstChildIndex() );
+ out.writeInt( ((SwitchValueInterpolator)node).getLastChildIndex() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ target = in.readInt();
+ ((SwitchValueInterpolator)node).setFirstChildIndex( in.readInt() );
+ ((SwitchValueInterpolator)node).setLastChildIndex( in.readInt() );
+ }
+
+
+ public void buildGraph() {
+ ((SwitchValueInterpolator)node).setTarget( (Switch)control.getSymbolTable().getJ3dNode( target ));
+ super.buildGraph(); // Must be last call in method
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] { javax.media.j3d.Alpha.class,
+ javax.media.j3d.Switch.class },
+ new Object[] { null,
+ null } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new SwitchValueInterpolator( null, null );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TexCoordGenerationState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TexCoordGenerationState.java
new file mode 100644
index 0000000..d29c5fb
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TexCoordGenerationState.java
@@ -0,0 +1,97 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.TexCoordGeneration;
+import javax.vecmath.Vector4f;
+
+public class TexCoordGenerationState extends NodeComponentState {
+
+ public TexCoordGenerationState( SymbolTableData symbol, Controller control ) {
+ super(symbol, control);
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ TexCoordGeneration attr = (TexCoordGeneration)node;
+ Vector4f vec = new Vector4f();
+ out.writeBoolean( attr.getEnable() );
+ out.writeInt( attr.getFormat() );
+ out.writeInt( attr.getGenMode() );
+ attr.getPlaneR( vec );
+ control.writeVector4f( out, vec );
+ attr.getPlaneS( vec );
+ control.writeVector4f( out, vec );
+ attr.getPlaneT( vec );
+ control.writeVector4f( out, vec );
+ attr.getPlaneQ( vec );
+ control.writeVector4f( out, vec );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ TexCoordGeneration attr = (TexCoordGeneration)node;
+ attr.setEnable( in.readBoolean() );
+ attr.setFormat( in.readInt() );
+ attr.setGenMode( in.readInt() );
+ attr.setPlaneR( control.readVector4f( in ));
+ attr.setPlaneS( control.readVector4f( in ));
+ attr.setPlaneT( control.readVector4f( in ));
+ attr.setPlaneQ( control.readVector4f( in ));
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new TexCoordGeneration();
+ }
+
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/Text3DState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/Text3DState.java
new file mode 100644
index 0000000..34f0f34
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/Text3DState.java
@@ -0,0 +1,115 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.Text3D;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Font3D;
+import javax.media.j3d.BoundingBox;
+import javax.vecmath.Point3f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class Text3DState extends GeometryState {
+
+ private int font3d;
+
+ public Text3DState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+
+ if (node!=null) {
+ font3d = control.getSymbolTable().addReference( ((Text3D)node).getFont3D() );
+ }
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeInt( ((Text3D)node).getAlignment() );
+ out.writeFloat( ((Text3D)node).getCharacterSpacing() );
+ out.writeInt( font3d );
+
+ out.writeInt( ((Text3D)node).getPath() );
+
+ Point3f pos = new Point3f();
+ ((Text3D)node).getPosition( pos );
+ control.writePoint3f( out, pos );
+
+ out.writeUTF( ((Text3D)node).getString() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ ((Text3D)node).setAlignment( in.readInt() );
+ ((Text3D)node).setCharacterSpacing( in.readFloat() );
+ font3d = in.readInt();
+ ((Text3D)node).setPath( in.readInt() );
+ ((Text3D)node).setPosition( control.readPoint3f(in ));
+ ((Text3D)node).setString( in.readUTF() );
+ }
+
+ /**
+ * Called when this component reference count is incremented.
+ * Allows this component to update the reference count of any components
+ * that it references.
+ */
+ public void addSubReference() {
+ control.getSymbolTable().incNodeComponentRefCount( font3d );
+ }
+
+ public void buildGraph() {
+ ((Text3D)node).setFont3D( ((Font3D)control.getSymbolTable().getJ3dNode( font3d )) );
+ super.buildGraph(); // Must be last call in method
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new Text3D();
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/Texture2DState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/Texture2DState.java
new file mode 100644
index 0000000..0a41f8e
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/Texture2DState.java
@@ -0,0 +1,140 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Texture2D;
+import javax.media.j3d.ImageComponent2D;
+
+public class Texture2DState extends TextureState {
+
+ private int detailImage=0;
+
+ public Texture2DState( SymbolTableData symbol, Controller control ) {
+ super(symbol, control);
+
+ // Set up references during save
+ if ( node!=null ) {
+ Texture2D t = (Texture2D)node;
+ detailImage = control.getSymbolTable().addReference( t.getDetailImage() );
+ }
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeInt( detailImage );
+
+ out.writeInt( ((Texture2D)node).getDetailTextureMode() );
+ out.writeInt( ((Texture2D)node).getDetailTextureLevel() );
+
+ int num = ((Texture2D)node).getDetailTextureFuncPointsCount();
+ out.writeInt( num );
+ float[] lod = new float[ num ];
+ float[] pts = new float[ num ];
+ ((Texture2D)node).getDetailTextureFunc( lod, pts );
+ for (int i = 0 ; i < num ; i++) {
+ out.writeFloat( lod[ i ] );
+ out.writeFloat( pts[ i ] );
+ }
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ detailImage = in.readInt();
+
+ ((Texture2D)node).setDetailTextureMode( in.readInt() );
+ ((Texture2D)node).setDetailTextureLevel( in.readInt() );
+ int num = in.readInt();
+ float[] lod = new float[ num ];
+ float[] pts = new float[ num ];
+ for (int i = 0 ; i < num ; i++) {
+ lod[ i ] = in.readFloat();
+ pts[ i ] = in.readFloat();
+ }
+ ((Texture2D)node).setDetailTextureFunc( lod, pts );
+ }
+
+ /**
+ * Called when this component reference count is incremented.
+ * Allows this component to update the reference count of any components
+ * that it references.
+ */
+ public void addSubReference() {
+ control.getSymbolTable().incNodeComponentRefCount( detailImage );
+ }
+
+ // Set up references during load
+ public void buildGraph() {
+ Texture2D t = (Texture2D)node;
+ t.setDetailImage( (ImageComponent2D)control.getSymbolTable().getJ3dNode( detailImage ) );
+ super.buildGraph(); // Must be last call in method
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] { Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE },
+ new Object[] { new Integer( mipMapMode ),
+ new Integer( format ),
+ new Integer( width ),
+ new Integer( height ),
+ new Integer( boundaryWidth ) } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new Texture2D( mipMapMode, format, width, height, boundaryWidth );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/Texture3DState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/Texture3DState.java
new file mode 100644
index 0000000..d81a57c
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/Texture3DState.java
@@ -0,0 +1,103 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Texture3D;
+
+public class Texture3DState extends TextureState {
+
+ private int depth;
+
+ public Texture3DState( SymbolTableData symbol, Controller control ) {
+ super(symbol, control);
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ out.writeInt( ((Texture3D)node).getBoundaryModeR() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ ((Texture3D)node).setBoundaryModeR( in.readInt() );
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+ out.writeInt( ((Texture3D)node).getDepth() );
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams( in );
+ depth = in.readInt();
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] { Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE },
+ new Object[] { new Integer( mipMapMode ),
+ new Integer( format ),
+ new Integer( width ),
+ new Integer( height ),
+ new Integer( depth ),
+ new Integer( boundaryWidth ) } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new Texture3D( mipMapMode, format, width, height, depth, boundaryWidth );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TextureAttributesState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TextureAttributesState.java
new file mode 100644
index 0000000..42adc87
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TextureAttributesState.java
@@ -0,0 +1,139 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.TextureAttributes;
+import javax.media.j3d.Transform3D;
+import javax.vecmath.Color4f;
+import javax.vecmath.Matrix4d;
+
+public class TextureAttributesState extends NodeComponentState {
+
+ private static final int MAX_COLOR_OPERANDS = 2;
+
+ public TextureAttributesState( SymbolTableData symbol, Controller control ) {
+ super(symbol, control);
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ TextureAttributes attr = (TextureAttributes)node;
+ Color4f clr = new Color4f();
+ Matrix4d mat = new Matrix4d();
+ Transform3D trans = new Transform3D();
+ int tableComponents = attr.getNumTextureColorTableComponents();
+ int tableSize = attr.getTextureColorTableSize();
+ int[][] colorTable = new int[tableComponents][tableSize];
+
+ out.writeInt( attr.getPerspectiveCorrectionMode() );
+ attr.getTextureBlendColor( clr );
+ control.writeColor4f( out, clr );
+ out.writeInt( tableComponents );
+ out.writeInt( tableSize );
+ attr.getTextureColorTable( colorTable );
+ for(int i=0; i<tableComponents; i++)
+ for(int j=0; j<tableSize; j++)
+ out.writeInt( colorTable[i][j] );
+
+ out.writeInt( attr.getTextureMode() );
+ attr.getTextureTransform( trans );
+ trans.get( mat );
+ control.writeMatrix4d( out, mat );
+
+ out.writeInt( attr.getCombineRgbMode() );
+ out.writeInt( attr.getCombineAlphaMode() );
+ for (int i = 0 ; i < MAX_COLOR_OPERANDS ; i++) {
+ out.writeInt( attr.getCombineRgbSource( i ) );
+ out.writeInt( attr.getCombineAlphaSource( i ) );
+ out.writeInt( attr.getCombineRgbFunction( i ) );
+ out.writeInt( attr.getCombineAlphaFunction( i ) );
+ }
+ out.writeInt( attr.getCombineRgbScale() );
+ out.writeInt( attr.getCombineAlphaScale() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ TextureAttributes attr = (TextureAttributes)node;
+ attr.setPerspectiveCorrectionMode( in.readInt() );
+ attr.setTextureBlendColor( control.readColor4f(in) );
+ int tableComponents = in.readInt();
+ int tableSize = in.readInt();
+ int[][] colorTable = new int[tableComponents][tableSize];
+ for(int i=0; i<tableComponents; i++)
+ for(int j=0; j<tableSize; j++)
+ colorTable[i][j] = in.readInt();
+ if (tableComponents!=0)
+ attr.setTextureColorTable( colorTable );
+ attr.setTextureMode( in.readInt() );
+ Matrix4d mat = control.readMatrix4d( in );
+ Transform3D trans = new Transform3D( mat );
+ attr.setTextureTransform( trans );
+
+ attr.setCombineRgbMode( in.readInt() );
+ attr.setCombineAlphaMode( in.readInt() );
+ for (int i = 0 ; i < MAX_COLOR_OPERANDS ; i++ ) {
+ attr.setCombineRgbSource( i, in.readInt() );
+ attr.setCombineAlphaSource( i, in.readInt() );
+ attr.setCombineRgbFunction( i, in.readInt() );
+ attr.setCombineAlphaFunction( i, in.readInt() );
+ }
+ attr.setCombineRgbScale( in.readInt() );
+ attr.setCombineAlphaScale( in.readInt() );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new TextureAttributes();
+ }
+
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TextureCubeMapState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TextureCubeMapState.java
new file mode 100644
index 0000000..0b8e1b6
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TextureCubeMapState.java
@@ -0,0 +1,117 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.TextureCubeMap;
+import javax.media.j3d.ImageComponent;
+import javax.media.j3d.ImageComponent2D;
+
+public class TextureCubeMapState extends TextureState {
+
+ private int[][] ic = new int[6][];
+
+ public TextureCubeMapState( SymbolTableData symbol, Controller control ) {
+ super(symbol, control);
+
+ if ( node!=null ) {
+ // Save references during save
+ TextureCubeMap tcm = (TextureCubeMap)node;
+
+ for ( int face=0; face<6; face++ ) {
+ ImageComponent[] images = tcm.getImages( face );
+ ic[ face ] = new int[ images.length ];
+ for(int i=0; i<images.length; i++) {
+ ic[ face ][ i ] = control.getSymbolTable().addReference( images[ i ] );
+ }
+ }
+ }
+ }
+
+ /**
+ * Called when this component reference count is incremented.
+ * Allows this component to update the reference count of any components
+ * that it references.
+ */
+ public void addSubReference() {
+ for ( int face=0; face<6; face++ ) {
+ for( int i=0; i<ic[ face ].length; i++)
+ control.getSymbolTable().incNodeComponentRefCount( ic[ face ][ i ] );
+ }
+ }
+
+ // Set up references during load
+ public void buildGraph() {
+ TextureCubeMap tcm = (TextureCubeMap)node;
+
+ for ( int face=0; face<6; face++ ) {
+ for( int i=0; i<ic[ face ].length; i++) {
+ tcm.setImage( i, face,
+ (ImageComponent2D)control.getSymbolTable().getJ3dNode( ic[ face ][ i ] ) );
+ }
+ }
+ super.buildGraph(); // Must be last call in method
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] { Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE },
+ new Object[] { new Integer( mipMapMode ),
+ new Integer( format ),
+ new Integer( width ),
+ new Integer( boundaryWidth ) } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new TextureCubeMap( mipMapMode, format, width, boundaryWidth );
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TextureState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TextureState.java
new file mode 100644
index 0000000..cae018b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TextureState.java
@@ -0,0 +1,231 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.ImageComponent;
+import javax.media.j3d.Texture;
+import javax.media.j3d.TextureCubeMap;
+import javax.vecmath.Color4f;
+import javax.vecmath.Point3f;
+
+public abstract class TextureState extends NodeComponentState {
+
+ private int[] imageComponents;
+ protected int width;
+ protected int height;
+ protected int format;
+ protected int mipMapMode;
+ protected int boundaryWidth;
+
+
+ public TextureState( SymbolTableData symbol, Controller control ) {
+ super(symbol, control);
+
+ if (node!=null) { // Node is null during a load
+ if ( !(node instanceof TextureCubeMap) ) {
+ ImageComponent[] images = ((Texture)node).getImages();
+ imageComponents = new int[ images.length ];
+ for(int i=0; i<images.length; i++) {
+ imageComponents[i] = control.getSymbolTable().addReference( images[i] );
+ }
+ }
+ }
+
+ }
+
+ public void writeConstructorParams( DataOutput out ) throws IOException {
+ super.writeConstructorParams( out );
+ out.writeInt( ((Texture)node).getMipMapMode() );
+ out.writeInt( ((Texture)node).getWidth() );
+ out.writeInt( ((Texture)node).getHeight() );
+ out.writeInt( ((Texture)node).getFormat() );
+ out.writeInt( ((Texture)node).getBoundaryWidth() );
+ }
+
+ public void readConstructorParams( DataInput in ) throws IOException {
+ super.readConstructorParams( in );
+
+ mipMapMode = in.readInt();
+ width = in.readInt();
+ height = in.readInt();
+ format = in.readInt();
+ boundaryWidth = in.readInt();
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ Texture attr = (Texture)node;
+ Color4f clr = new Color4f();
+ attr.getBoundaryColor( clr );
+ control.writeColor4f( out, clr );
+ out.writeInt( attr.getBoundaryModeS() );
+ out.writeInt( attr.getBoundaryModeT() );
+ out.writeBoolean( attr.getEnable() );
+
+ out.writeInt( imageComponents.length );
+ for(int i=0; i<imageComponents.length; i++)
+ out.writeInt( imageComponents[i] );
+
+ out.writeInt( attr.getMagFilter() );
+ out.writeInt( attr.getMinFilter() );
+ out.writeInt( attr.getBaseLevel() );
+ out.writeInt( attr.getMaximumLevel() );
+ out.writeFloat( attr.getMinimumLOD() );
+ out.writeFloat( attr.getMaximumLOD() );
+
+ Point3f lodOffset = new Point3f();
+ attr.getLodOffset( lodOffset );
+ control.writePoint3f( out, lodOffset );
+
+ out.writeInt( attr.getAnisotropicFilterMode() );
+ out.writeFloat( attr.getAnisotropicFilterDegree() );
+
+ int points = attr.getSharpenTextureFuncPointsCount();
+ out.writeInt( points );
+ if ( points>0 ) {
+ float[] lod = new float[ points ];
+ float[] pts = new float[ points ];
+ attr.getSharpenTextureFunc( lod, pts );
+ for (int i = 0 ; i < points ; i++) {
+ out.writeFloat( lod[i] );
+ out.writeFloat( pts[i] );
+ }
+ }
+
+ points = attr.getFilter4FuncPointsCount();
+ out.writeInt( points );
+ if ( points>=4 ) {
+ float[] weights = new float[ points ];
+ attr.getFilter4Func( weights );
+ for (int i = 0 ; i < points ; i++) {
+ out.writeFloat( weights[i] );
+ }
+ }
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ Texture attr = (Texture)node;
+ attr.setBoundaryColor( control.readColor4f( in ));
+ attr.setBoundaryModeS( in.readInt() );
+ attr.setBoundaryModeT( in.readInt() );
+ attr.setEnable( in.readBoolean() );
+
+ imageComponents = new int[ in.readInt() ];
+ for(int i=0; i<imageComponents.length; i++)
+ imageComponents[i] = in.readInt();
+
+ int mag = in.readInt();
+ try {
+ attr.setMagFilter( mag );
+ } catch(IllegalArgumentException e ) {
+ // The OpenFLT loader sets erroneous values for
+ // mag filter which will cause an exception with
+ // Java3D 1.3, handle this gracefully....
+ if (mag==Texture.MULTI_LEVEL_LINEAR)
+ attr.setMagFilter( Texture.BASE_LEVEL_LINEAR );
+ else if (mag==Texture.MULTI_LEVEL_POINT)
+ attr.setMagFilter( Texture.BASE_LEVEL_POINT );
+ else
+ attr.setMagFilter( Texture.FASTEST );
+ }
+
+ attr.setMinFilter( in.readInt() );
+
+ attr.setBaseLevel( in.readInt() );
+ attr.setMaximumLevel( in.readInt() );
+ attr.setMinimumLOD( in.readFloat() );
+ attr.setMaximumLOD( in.readFloat() );
+ attr.setLodOffset( control.readPoint3f( in ) );
+ attr.setAnisotropicFilterMode( in.readInt() );
+ attr.setAnisotropicFilterDegree( in.readFloat() );
+
+ int points = in.readInt();
+ if ( points>0 ) {
+ float[] lod = new float[ points ];
+ float[] pts = new float[ points ];
+ for (int i = 0 ; i < points ; i++) {
+ lod[i] = in.readFloat();
+ pts[i] = in.readFloat();
+ }
+ attr.setSharpenTextureFunc( lod, pts );
+ }
+
+ points = in.readInt();
+ if ( points >= 4 ) {
+ float[] weights = new float[ points ];
+ for (int i = 0 ; i < points ; i++) {
+ weights[i] = in.readFloat();
+ }
+ attr.setFilter4Func( weights );
+ }
+ }
+
+ public void addSubReference() {
+ if ( !(node instanceof TextureCubeMap) ) {
+ for( int i=0; i<imageComponents.length; i++)
+ control.getSymbolTable().incNodeComponentRefCount( imageComponents[i] );
+ }
+ }
+
+ public void buildGraph() {
+ if ( !(node instanceof TextureCubeMap) ) {
+ for(int i=0; i<imageComponents.length; i++) {
+ ((Texture)node).setImage( i,
+ (ImageComponent)control.getSymbolTable().getJ3dNode( imageComponents[i] ));
+ }
+ }
+
+ super.buildGraph(); // Must be last call in method
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TextureUnitStateState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TextureUnitStateState.java
new file mode 100644
index 0000000..f700136
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TextureUnitStateState.java
@@ -0,0 +1,111 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.TextureUnitState;
+import javax.media.j3d.Texture;
+import javax.media.j3d.TextureAttributes;
+import javax.media.j3d.TexCoordGeneration;
+import javax.vecmath.Color3f;
+
+public class TextureUnitStateState extends NodeComponentState {
+
+ private int texCoordGeneration;
+ private int texture;
+ private int textureAttributes;
+
+ public TextureUnitStateState(SymbolTableData symbol,Controller control) {
+ super(symbol, control);
+
+ if (node!=null) {
+ TextureUnitState attr = (TextureUnitState)node;
+ texCoordGeneration = control.getSymbolTable().addReference( attr.getTexCoordGeneration() );
+ texture = control.getSymbolTable().addReference( attr.getTexture() );
+ textureAttributes =control.getSymbolTable().addReference( attr.getTextureAttributes() );
+ }
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ out.writeInt( texCoordGeneration );
+ out.writeInt( texture );
+ out.writeInt( textureAttributes );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ TextureUnitState attr = (TextureUnitState)node;
+ texCoordGeneration = in.readInt();
+ texture = in.readInt();
+ textureAttributes = in.readInt();
+ }
+
+ public void addSubReference() {
+ control.getSymbolTable().incNodeComponentRefCount( texCoordGeneration );
+ control.getSymbolTable().incNodeComponentRefCount( texture );
+ control.getSymbolTable().incNodeComponentRefCount( textureAttributes );
+ }
+
+ public void buildGraph() {
+ TextureUnitState attr = (TextureUnitState)node;
+ attr.setTexCoordGeneration( (TexCoordGeneration)control.getSymbolTable().getJ3dNode( texCoordGeneration ));
+ attr.setTexture( (Texture)control.getSymbolTable().getJ3dNode( texture ));
+ attr.setTextureAttributes( (TextureAttributes)control.getSymbolTable().getJ3dNode( textureAttributes ));
+ super.buildGraph(); // Must be last call in method
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new TextureUnitState();
+ }
+
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TransformGroupState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TransformGroupState.java
new file mode 100644
index 0000000..ea612d4
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TransformGroupState.java
@@ -0,0 +1,91 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.TransformGroup;
+import javax.media.j3d.Transform3D;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class TransformGroupState extends GroupState {
+
+ /** Creates new BranchGroupState */
+ public TransformGroupState( SymbolTableData symbol, Controller control ) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ Transform3D trans = new Transform3D();
+ ((TransformGroup)node).getTransform( trans );
+ double[] matrix = new double[16];
+ trans.get( matrix );
+
+ for(int i=0; i<16; i++)
+ out.writeDouble( matrix[i] );
+
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ Transform3D trans = new Transform3D();
+ double[] matrix = new double[16];
+
+ for(int i=0; i<16; i++)
+ matrix[i] = in.readDouble();
+
+ trans.set( matrix );
+ ((TransformGroup)node).setTransform( trans );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new TransformGroup();
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TransformInterpolatorState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TransformInterpolatorState.java
new file mode 100644
index 0000000..3a51970
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TransformInterpolatorState.java
@@ -0,0 +1,88 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.Interpolator;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Alpha;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.TransformGroup;
+import javax.media.j3d.TransformInterpolator;
+
+public abstract class TransformInterpolatorState extends InterpolatorState {
+
+ private int target=0;
+
+ public TransformInterpolatorState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ TransformInterpolator ti = (TransformInterpolator)node;
+
+ control.writeTransform3D(out, ti.getTransformAxis() );
+ out.writeInt( control.getSymbolTable().addReference( ti.getTarget() ) );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ TransformInterpolator ti = (TransformInterpolator)node;
+ ti.setTransformAxis( control.readTransform3D( in ) );
+ target = in.readInt();
+ }
+
+ public void buildGraph() {
+ ((TransformInterpolator)node).setTarget(
+ (TransformGroup)control.getSymbolTable().getJ3dNode( target ) );
+ super.buildGraph(); // Must be last call in method
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TransparencyAttributesState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TransparencyAttributesState.java
new file mode 100644
index 0000000..62683b3
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TransparencyAttributesState.java
@@ -0,0 +1,86 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.TransparencyAttributes;
+import javax.vecmath.Color3f;
+
+public class TransparencyAttributesState extends NodeComponentState {
+
+ public TransparencyAttributesState(SymbolTableData symbol,Controller control) {
+ super(symbol, control);
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ TransparencyAttributes attr = (TransparencyAttributes)node;
+ out.writeInt( attr.getDstBlendFunction() );
+ out.writeInt( attr.getSrcBlendFunction() );
+ out.writeFloat( attr.getTransparency() );
+ out.writeInt( attr.getTransparencyMode() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ TransparencyAttributes attr = (TransparencyAttributes)node;
+ attr.setDstBlendFunction( in.readInt() );
+ attr.setSrcBlendFunction( in.readInt() );
+ attr.setTransparency( in.readFloat() );
+ attr.setTransparencyMode( in.readInt() );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new TransparencyAttributes();
+ }
+
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TransparencyInterpolatorState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TransparencyInterpolatorState.java
new file mode 100644
index 0000000..eb05a2b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TransparencyInterpolatorState.java
@@ -0,0 +1,112 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.TransparencyInterpolator;
+import javax.media.j3d.TransparencyAttributes;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.TransformGroup;
+import javax.vecmath.Point3f;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class TransparencyInterpolatorState extends InterpolatorState {
+
+ private int target;
+
+ public TransparencyInterpolatorState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+
+ if (node!=null)
+ target = control.getSymbolTable().addReference( ((TransparencyInterpolator)node).getTarget() );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeInt( target );
+ out.writeFloat( ((TransparencyInterpolator)node).getMinimumTransparency() );
+ out.writeFloat( ((TransparencyInterpolator)node).getMaximumTransparency() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ target = in.readInt();
+ ((TransparencyInterpolator)node).setMinimumTransparency( in.readFloat() );
+ ((TransparencyInterpolator)node).setMaximumTransparency( in.readFloat() );
+ }
+
+ /**
+ * Called when this component reference count is incremented.
+ * Allows this component to update the reference count of any components
+ * that it references.
+ */
+ public void addSubReference() {
+ control.getSymbolTable().incNodeComponentRefCount( target );
+ }
+
+ public void buildGraph() {
+ ((TransparencyInterpolator)node).setTarget( (TransparencyAttributes)control.getSymbolTable().getJ3dNode( target ));
+ super.buildGraph(); // Must be last call in method
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] { javax.media.j3d.Alpha.class,
+ javax.media.j3d.TransparencyAttributes.class },
+ new Object[] { null,
+ null } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new TransparencyInterpolator( null, null );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TriangleArrayState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TriangleArrayState.java
new file mode 100644
index 0000000..1e2eff8
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TriangleArrayState.java
@@ -0,0 +1,92 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import javax.media.j3d.TriangleArray;
+import javax.media.j3d.SceneGraphObject;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class TriangleArrayState extends GeometryArrayState {
+
+ public TriangleArrayState( SymbolTableData symbol, Controller control ) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ }
+
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] {
+ Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ texCoordSetMap.getClass()
+ },
+ new Object[] { new Integer( vertexCount ),
+ new Integer( vertexFormat ),
+ new Integer( texCoordSetCount ),
+ texCoordSetMap } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new TriangleArray( vertexCount, vertexFormat, texCoordSetCount, texCoordSetMap );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TriangleFanArrayState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TriangleFanArrayState.java
new file mode 100644
index 0000000..049858d
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TriangleFanArrayState.java
@@ -0,0 +1,94 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import javax.media.j3d.TriangleFanArray;
+import javax.media.j3d.SceneGraphObject;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class TriangleFanArrayState extends GeometryStripArrayState {
+
+ public TriangleFanArrayState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ }
+
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ }
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] {
+ Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ texCoordSetMap.getClass(),
+ stripVertexCounts.getClass()
+ },
+ new Object[] { new Integer( vertexCount ),
+ new Integer( vertexFormat ),
+ new Integer( texCoordSetCount ),
+ texCoordSetMap,
+ stripVertexCounts } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new TriangleFanArray( vertexCount, vertexFormat, texCoordSetCount, texCoordSetMap, stripVertexCounts );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TriangleStripArrayState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TriangleStripArrayState.java
new file mode 100644
index 0000000..04da7b4
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/TriangleStripArrayState.java
@@ -0,0 +1,95 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import javax.media.j3d.TriangleStripArray;
+import javax.media.j3d.SceneGraphObject;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class TriangleStripArrayState extends GeometryStripArrayState {
+
+ public TriangleStripArrayState( SymbolTableData symbol, Controller control ) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+ }
+
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+ }
+
+
+ public SceneGraphObject createNode( Class j3dClass ) {
+ return createNode( j3dClass, new Class[] {
+ Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ texCoordSetMap.getClass(),
+ stripVertexCounts.getClass()
+ },
+ new Object[] { new Integer( vertexCount ),
+ new Integer( vertexFormat ),
+ new Integer( texCoordSetCount ),
+ texCoordSetMap,
+ stripVertexCounts } );
+ }
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new TriangleStripArray( vertexCount, vertexFormat, texCoordSetCount, texCoordSetMap, stripVertexCounts );
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ViewPlatformState.java b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ViewPlatformState.java
new file mode 100644
index 0000000..5d2e734
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/scenegraph/io/state/javax/media/j3d/ViewPlatformState.java
@@ -0,0 +1,83 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import javax.media.j3d.ViewPlatform;
+import javax.media.j3d.SceneGraphObject;
+import com.sun.j3d.utils.scenegraph.io.retained.Controller;
+import com.sun.j3d.utils.scenegraph.io.retained.SymbolTableData;
+
+public class ViewPlatformState extends LeafState {
+
+ private int[] geometry;
+ private int appearance;
+
+ public ViewPlatformState(SymbolTableData symbol,Controller control) {
+ super( symbol, control );
+ }
+
+ public void writeObject( DataOutput out ) throws IOException {
+ super.writeObject( out );
+
+ out.writeFloat( ((ViewPlatform)node).getActivationRadius() );
+ out.writeInt( ((ViewPlatform)node).getViewAttachPolicy() );
+ }
+
+ public void readObject( DataInput in ) throws IOException {
+ super.readObject( in );
+
+ ((ViewPlatform)node).setActivationRadius( in.readFloat() );
+ ((ViewPlatform)node).setViewAttachPolicy( in.readInt() );
+ }
+
+
+ protected javax.media.j3d.SceneGraphObject createNode() {
+ return new ViewPlatform();
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/timer/J3DTimer.java b/src/classes/share/com/sun/j3d/utils/timer/J3DTimer.java
new file mode 100644
index 0000000..5fb6a6b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/timer/J3DTimer.java
@@ -0,0 +1,102 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+/**
+ * A High Resolution operating system dependent interval timer.
+ */
+package com.sun.j3d.utils.timer;
+
+/**
+ *
+ * A High Resolution interval timer. The timer resolution is
+ * operating system dependent and can be queried using
+ * getTimerResolution().
+ *
+ * These methods are not reentrant and should not
+ * be called concurrently from multiple threads.
+ *
+ */
+public class J3DTimer {
+
+ /**
+ * Private constructor because users should
+ * not construct instances of this class
+ */
+ private J3DTimer() {
+ }
+
+ /**
+ * Get the timer value, in nanoseconds.
+ * The initial value of the timer is OS dependent.
+ *
+ * @return The current timer value in nanoseconds.
+ */
+ public static long getValue() {
+ return getNativeTimer();
+ }
+
+ /**
+ * Get the nanosecond resolution of the timer
+ *
+ * @return The timer resolution in nanoseconds.
+ */
+ public static long getResolution() {
+ return getNativeTimerResolution();
+ }
+
+ private static native long getNativeTimer();
+
+ private static native long getNativeTimerResolution();
+
+ static {
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ System.loadLibrary("j3dutils");
+ return null;
+ }
+ });
+
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/timer/package.html b/src/classes/share/com/sun/j3d/utils/timer/package.html
new file mode 100644
index 0000000..6fbf707
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/timer/package.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+
+<!--
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+ -->
+
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=iso-8859-1">
+ <TITLE>com.sun.j3d.utils.timer</TITLE>
+</HEAD>
+<BODY>
+<P> A High Resolution operating system dependent interval timer.</P>
+</BODY>
+</HTML>
diff --git a/src/classes/share/com/sun/j3d/utils/universe/ConfigCommand.java b/src/classes/share/com/sun/j3d/utils/universe/ConfigCommand.java
new file mode 100644
index 0000000..cb0e412
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/ConfigCommand.java
@@ -0,0 +1,417 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.universe ;
+
+import java.text.DecimalFormat ;
+import java.text.FieldPosition ;
+import java.util.Collection ;
+import javax.vecmath.Matrix3d ;
+import javax.vecmath.Matrix4d ;
+
+/**
+ * Contains the elements which compose a configuration file command,
+ * including the command name, type, and arguments.
+ */
+class ConfigCommand {
+ /**
+ * Specifies that this command creates a new ConfigObject.
+ */
+ static final int CREATE = 0 ;
+
+ /**
+ * Specifies that this command sets an attribute for a class known
+ * to ConfiguredUniverse. As of Java 3D 1.3.1, these commands are
+ * handled the same as property commands (see PROPERTY below) and
+ * this constant is no longer used.
+ */
+ static final int ATTRIBUTE = 1 ;
+
+ /**
+ * Specifies that this command sets a Java system property or a
+ * property for a class unknown to ConfiguredUniverse. Properties
+ * for such a class are set by using the reflection API to invoke a
+ * method whose name is specified in the command's argument list.
+ * Such a method must accept an array of Object as its sole
+ * parameter, where that array contains all the command elements
+ * which appear after the method name.<p>
+ *
+ * As of Java 3D 1.3.1, this is handled the same as an attribute.
+ * The individual setProperty() method implementations of
+ * ConfigObject determine whether the method to set the property can
+ * be invoked directly or through introspection. If through
+ * introspection, then the evaluation of the property must be
+ * delayed until the target object is instantiated.
+ */
+ static final int PROPERTY = 2 ;
+
+ /**
+ * Specifies that this command creates an alias for a ConfigObject of the
+ * same base name.
+ */
+ static final int ALIAS = 3 ;
+
+ /**
+ * Specifies that this command is a deferred built-in command that can't
+ * be immediately evaluated by the parser. Its evaluation is delayed
+ * until all config objects are instantiated and their properties can be
+ * evaluated.
+ */
+ static final int BUILTIN = 4 ;
+
+ /**
+ * Specifies that this command is an include file directive.
+ */
+ static final int INCLUDE = 5 ;
+
+ /**
+ * Specifes that this command is entirely processed by the
+ * constructor and should be ignored by subsequent recipients.
+ */
+ static final int IGNORE = 6 ;
+
+ /**
+ * The type of this command, either CREATE, PROPERTY, ALIAS,
+ * BUILTIN, INCLUDE, or IGNORE.
+ */
+ int type = -1 ;
+
+ /**
+ * The number of arguments in this command, including the command
+ * name.
+ */
+ int argc = 0 ;
+
+ /**
+ * An array containing all of this command's arguments, including
+ * the command name.
+ */
+ Object[] argv = null ;
+
+ /**
+ * The name of the command being invoked, which is always the first
+ * argument of the command.
+ */
+ String commandName = null ;
+
+ /**
+ * The base name of this command, from which the name of the ConfigObject
+ * subclass that processes it is derived. This is constructed by
+ * stripping off the leading "New" prefix or the trailing "Attribute",
+ * "Property", or "Alias" suffix of the command name. The name of the
+ * ConfigObject subclass which handles the command is derived by adding
+ * "Config" as a prefix to the base name.
+ */
+ String baseName = null ;
+
+ /**
+ * The instance name of the ConfigObject subclass which processes this
+ * command. Together with the base name this provides the handle by which
+ * a ConfigObject can be referenced by other commands in the configuration
+ * file.
+ */
+ String instanceName = null ;
+
+ /**
+ * The file from which this command was read.
+ */
+ String fileName = null ;
+
+ /**
+ * The line number from which this command was read.
+ */
+ int lineNumber = 0 ;
+
+ /**
+ * Constructs a ConfigCommand from configuration file command arguments.
+ *
+ * @param elements arguments to this command, including the command name
+ * @param fileName name of the file from where the command was read
+ * @param lineNumber line number where the command is found in the file
+ */
+ ConfigCommand(Collection elements, String fileName, int lineNumber) {
+ this.fileName = fileName ;
+ this.lineNumber = lineNumber ;
+
+ argc = elements.size() ;
+ argv = elements.toArray(new Object[0]) ;
+
+ if (! (argc > 0 && (argv[0] instanceof String)))
+ throw new IllegalArgumentException("malformed command") ;
+
+ commandName = (String)argv[0] ;
+
+ if (commandName.startsWith("New")) {
+ type = CREATE ;
+ baseName = commandName.substring(3) ;
+ instanceName = checkName(argv[1]) ;
+ }
+ else if (commandName.endsWith("Property")) {
+ baseName = commandName.substring(0, commandName.length()-8) ;
+ if (baseName.equals("Java")) {
+ type = IGNORE ;
+ processJavaProperty(argc, argv) ;
+ }
+ else {
+ type = PROPERTY ;
+ instanceName = checkName(argv[1]) ;
+ }
+ }
+ else if (commandName.endsWith("Attribute")) {
+ // Backward compatibility.
+ type = PROPERTY ;
+ baseName = commandName.substring(0, commandName.length()-9) ;
+ instanceName = checkName(argv[1]) ;
+ }
+ else if (commandName.endsWith("Alias")) {
+ type = ALIAS ;
+ baseName = commandName.substring(0, commandName.length()-5) ;
+ instanceName = checkName(argv[1]) ;
+ }
+ else if (commandName.equals("Include")) {
+ type = INCLUDE ;
+ }
+ else {
+ type = BUILTIN ;
+ }
+
+ // We allow "Window" as an equivalent to "Screen".
+ if (baseName != null && baseName.equals("Window"))
+ baseName = "Screen" ;
+ }
+
+ /**
+ * Sets the Java property specified in the command. If the command
+ * has 3 arguments then it's an unconditional assignment. If the
+ * 3rd argument is "Default", then the property is set to the value
+ * of the 4th argument only if the specified property has no
+ * existing value.
+ *
+ * @param argc the number of arguments in the command
+ * @param argv command arguments as an array of Objects; the 1st is
+ * the command name (ignored), the 2nd is the name of the Java
+ * property, the 3rd is the value to be set or the keyword
+ * "Default", and the 4th is thevalue to be set if the Java
+ * property doesn't already exist
+ */
+ private static void processJavaProperty(int argc, Object[] argv) {
+ for (int i = 1 ; i < argc ; i++) {
+ // Check args.
+ if (argv[i] instanceof Boolean) {
+ argv[i] = ((Boolean)argv[i]).toString() ;
+ }
+ else if (! (argv[i] instanceof String)) {
+ throw new IllegalArgumentException
+ ("JavaProperty arguments must be Strings or Booleans") ;
+ }
+ }
+ if (argc == 3) {
+ // Unconditional assignment.
+ setJavaProperty((String)argv[1], (String)argv[2]) ;
+ }
+ else if (argc != 4) {
+ // Conditional assignment must have 4 args.
+ throw new IllegalArgumentException
+ ("JavaProperty must have either 2 or 3 arguments") ;
+ }
+ else if (! ((String)argv[2]).equals("Default")) {
+ // Penultimate arg must be "Default" keyword.
+ throw new IllegalArgumentException
+ ("JavaProperty 2nd argument must be \"Default\"") ;
+ }
+ else if (evaluateJavaProperty((String)argv[1]) == null) {
+ // Assignment only if no existing value.
+ setJavaProperty((String)argv[1], (String)argv[3]) ;
+ }
+ }
+
+ /**
+ * Sets the given Java system property if allowed by the security manager.
+ *
+ * @param key property name
+ * @param value property value
+ * @return previous property value if any
+ */
+ static String setJavaProperty(final String key, final String value) {
+ return (String)java.security.AccessController.doPrivileged
+ (new java.security.PrivilegedAction() {
+ public Object run() {
+ return System.setProperty(key, value) ;
+ }
+ }) ;
+ }
+
+ /**
+ * Evaluates the specified Java property string if allowed by the security
+ * manager.
+ *
+ * @param key string containing a Java property name
+ * @return string containing the Java property valaue
+ */
+ static String evaluateJavaProperty(final String key) {
+ return (String)java.security.AccessController.doPrivileged
+ (new java.security.PrivilegedAction() {
+ public Object run() {
+ return System.getProperty(key) ;
+ }
+ }) ;
+ }
+
+ /**
+ * Checks if the given object is an instance of String.
+ *
+ * @param o the object to be checked
+ * @return the object cast to a String
+ * @exception IllegalArgumentException if the object is not a String
+ */
+ private final String checkName(Object o) {
+ if (! (o instanceof String))
+ throw new IllegalArgumentException
+ ("second argument to \"" + commandName + "\" must be a name") ;
+
+ return (String)o ;
+ }
+
+ /**
+ * Calls <code>formatMatrixRows(3, 3, m)</code>, where <code>m</code> is a
+ * an array of doubles retrieved from the given Matrix3d.
+ *
+ * @param m3 matrix to be formatted
+ * @return matrix rows formatted into strings
+ */
+ static String[] formatMatrixRows(Matrix3d m3) {
+ double[] m = new double[9] ;
+ m[0] = m3.m00 ; m[1] = m3.m01 ; m[2] = m3.m02 ;
+ m[3] = m3.m10 ; m[4] = m3.m11 ; m[5] = m3.m12 ;
+ m[6] = m3.m20 ; m[7] = m3.m21 ; m[8] = m3.m22 ;
+
+ return formatMatrixRows(3, 3, m) ;
+ }
+
+ /**
+ * Calls <code>formatMatrixRows(4, 4, m)</code>, where <code>m</code> is a
+ * an array of doubles retrieved from the given Matrix4d.
+ *
+ * @param m4 matrix to be formatted
+ * @return matrix rows formatted into strings
+ */
+ static String[] formatMatrixRows(Matrix4d m4) {
+ double[] m = new double[16] ;
+ m[0] = m4.m00 ; m[1] = m4.m01 ; m[2] = m4.m02 ; m[3] = m4.m03 ;
+ m[4] = m4.m10 ; m[5] = m4.m11 ; m[6] = m4.m12 ; m[7] = m4.m13 ;
+ m[8] = m4.m20 ; m[9] = m4.m21 ; m[10] = m4.m22 ; m[11] = m4.m23 ;
+ m[12] = m4.m30 ; m[13] = m4.m31 ; m[14] = m4.m32 ; m[15] = m4.m33 ;
+
+ return formatMatrixRows(4, 4, m) ;
+ }
+
+ /**
+ * Formats a matrix with fixed fractional digits and integer padding to
+ * align the decimal points in columns. Non-negative numbers print up to
+ * 7 integer digits, while negative numbers print up to 6 integer digits
+ * to account for the negative sign. 6 fractional digits are printed.
+ *
+ * @param rowCount number of rows in the matrix
+ * @param colCount number of columns in the matrix
+ * @param m matrix to be formatted
+ * @return matrix rows formatted into strings
+ */
+ static String[] formatMatrixRows(int rowCount, int colCount, double[] m) {
+ DecimalFormat df = new DecimalFormat("0.000000") ;
+ FieldPosition fp = new FieldPosition(DecimalFormat.INTEGER_FIELD) ;
+ StringBuffer sb0 = new StringBuffer() ;
+ StringBuffer sb1 = new StringBuffer() ;
+ String[] rows = new String[rowCount] ;
+
+ for (int i = 0 ; i < rowCount ; i++) {
+ sb0.setLength(0) ;
+ for (int j = 0 ; j < colCount ; j++) {
+ sb1.setLength(0) ;
+ df.format(m[i*colCount+j], sb1, fp) ;
+ int pad = 8 - fp.getEndIndex() ;
+ for (int k = 0 ; k < pad ; k++) {
+ sb1.insert(0, " ") ;
+ }
+ sb0.append(sb1) ;
+ }
+ rows[i] = sb0.toString() ;
+ }
+ return rows ;
+ }
+
+ /**
+ * Returns the String representation of this command.
+ *
+ * @return string representing this command
+ */
+ public String toString() {
+ String[] lines = null ;
+ StringBuffer sb = new StringBuffer("(") ;
+
+ for (int i = 0 ; i < argc ; i++) {
+ if (argv[i] instanceof Matrix3d) {
+ lines = formatMatrixRows((Matrix3d)argv[i]) ;
+ sb.append("\n ((" + lines[0] + ")\n") ;
+ sb.append(" (" + lines[1] + ")\n") ;
+ sb.append(" (" + lines[2] + "))") ;
+ if (i != (argc - 1)) sb.append("\n") ;
+ }
+ else if (argv[i] instanceof Matrix4d) {
+ lines = formatMatrixRows((Matrix4d)argv[i]) ;
+ sb.append("\n ((" + lines[0] + ")\n") ;
+ sb.append(" (" + lines[1] + ")\n") ;
+ sb.append(" (" + lines[2] + ")\n") ;
+ sb.append(" (" + lines[3] + "))") ;
+ if (i != (argc - 1)) sb.append("\n") ;
+ }
+ else {
+ if (i > 0) sb.append(" ") ;
+ sb.append(argv[i].toString()) ;
+ }
+ }
+
+ sb.append(")") ;
+ return sb.toString() ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/universe/ConfigContainer.java b/src/classes/share/com/sun/j3d/utils/universe/ConfigContainer.java
new file mode 100644
index 0000000..acbc984
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/ConfigContainer.java
@@ -0,0 +1,1479 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.universe ;
+
+import java.io.* ;
+import java.util.* ;
+import java.net.URL ;
+import java.net.MalformedURLException ;
+import javax.media.j3d.* ;
+import com.sun.j3d.utils.behaviors.vp.ViewPlatformBehavior ;
+
+/**
+ * Loads a Java 3D configuration file and creates a container of named objects
+ * that will effect the viewing configuration specified in the file. These
+ * can include Viewers, ViewingPlatforms, ViewPlatformBehaviors, InputDevices,
+ * Sensors, and other objects.<p>
+ *
+ * Clients can construct the view side of a scene graph by retrieving these
+ * objects using the accessor methods provided by this class. This could
+ * involve as little as just attaching ViewingPlatforms to a Locale, depending
+ * upon how completely the viewing configuration is specified in the file.
+ * The ConfiguredUniverse class is an example of a ConfigContainer client and
+ * how it can be used.<p>
+ *
+ * ConfigContainer can be useful for clients other than ConfiguredUniverse.
+ * InputDevice and ViewPlatformBehavior configuration is fully supported, so a
+ * given Java 3D installation can provide configuration files to an
+ * application that will allow it to fully utilize whatever site-specific
+ * devices and behaviors are available. The configuration mechanism can be
+ * extended for any target object through the use of the
+ * <code>NewObject</code> and <code>ObjectProperty</code> configuration
+ * commands.
+ *
+ * @see ConfiguredUniverse
+ * @see <a href="doc-files/config-syntax.html">
+ * The Java 3D Configuration File</a>
+ * @see <a href="doc-files/config-examples.html">
+ * Example Configuration Files</a>
+ *
+ * @since Java 3D 1.3.1
+ */
+public class ConfigContainer {
+ //
+ // The configuration object database is implemented with a HashMap which
+ // maps their class names to ArrayList objects which contain the actual
+ // instances. The latter are used since the instances of a given class
+ // must be evaluated in the order in which they were created.
+ // LinkedHashMap is available in JDK 1.4 but currently this code must run
+ // under JDK 1.3.1 as well.
+ //
+ private Map baseNameMap = new HashMap() ;
+
+ // Map containing named canvases for each view.
+ private Map viewCanvasMap = new HashMap() ;
+
+ // Read-only Maps for the public interface to the configuration database.
+ private ReadOnlyMap bodyMap = null ;
+ private ReadOnlyMap environmentMap = null ;
+ private ReadOnlyMap viewerMap = null ;
+ private ReadOnlyMap deviceMap = null ;
+ private ReadOnlyMap sensorMap = null ;
+ private ReadOnlyMap behaviorMap = null ;
+ private ReadOnlyMap platformMap = null ;
+ private ReadOnlyMap genericObjectMap = null ;
+
+ // Read-only Sets for the public interface to the configuration database.
+ private ReadOnlySet bodies = null ;
+ private ReadOnlySet environments = null ;
+ private ReadOnlySet viewers = null ;
+ private ReadOnlySet devices = null ;
+ private ReadOnlySet sensors = null ;
+ private ReadOnlySet behaviors = null ;
+ private ReadOnlySet platforms = null ;
+ private ReadOnlySet genericObjects = null ;
+
+ // The number of TransformGroups to include in ViewingPlatforms.
+ private int transformCount = 1 ;
+
+ // The visibility status of Viewer AWT components.
+ private boolean setVisible = false ;
+
+ /**
+ * The name of the file this ConfigContainer is currently loading.
+ */
+ String currentFileName = null ;
+
+ /**
+ * Creates a new ConfigContainer and loads the configuration file at the
+ * specified URL. All ViewingPlatform instances are created with a single
+ * TransformGroup and all Viewer components are initially invisible.
+ *
+ * @param userConfig URL of the configuration file to load
+ */
+ public ConfigContainer(URL userConfig) {
+ this(userConfig, false, 1, true) ;
+ }
+
+ /**
+ * Creates a new ConfigContainer and loads the configuration file at the
+ * specified URL. Any ViewingPlatform instantiated by the configuration
+ * file will be created with the specified number of transforms. Viewer
+ * components may be set initially visible or invisible with the
+ * <code>setVisible</code> flag.
+ *
+ * @param userConfig URL of the configuration file to load
+ * @param setVisible if true, <code>setVisible(true)</code> is called on
+ * all Viewers
+ * @param transformCount number of transforms to be included in any
+ * ViewingPlatform created; must be greater than 0
+ */
+ public ConfigContainer(URL userConfig,
+ boolean setVisible, int transformCount) {
+
+ this(userConfig, setVisible, transformCount, true) ;
+ }
+
+ /**
+ * Package-scoped constructor for ConfigContainer. This provides an
+ * additional flag, <code>attachBehaviors</code>, which indicates whether
+ * or not ViewPlatformBehaviors should be attached to the ViewingPlatforms
+ * specified for them.<p>
+ *
+ * Normally the flag should be true. However, when instantiated by
+ * ConfiguredUniverse, this flag is set false so that ConfiguredUniverse
+ * can set a reference to itself in the ViewingPlatform before attaching
+ * the behavior. This provides backwards compatibility to behaviors that
+ * access the ConfiguredUniverse instance from a call to
+ * <code>setViewingPlatform</code> in order to look up the actual Sensor,
+ * Viewer, Behavior, etc., instances associated with the names provided
+ * them from the configuration file.<p>
+ *
+ * The preferred methods to retrieve instances of specific objects defined
+ * in the configuration file are to either 1) get the ConfiguredUniverse
+ * instance when the behavior's <code>initialize</code> method is called,
+ * or to 2) define properties that accept object instances directly, and
+ * then use the newer Device, Sensor, ViewPlatform, etc., built-in
+ * commands in the configuration file. These built-ins will return an
+ * object instance from a name.
+ *
+ * @param userConfig URL of the configuration file to load
+ * @param setVisible if true, <code>setVisible(true)</code> is called on
+ * all Viewers
+ * @param transformCount number of transforms to be included in any
+ * ViewingPlatform created; must be greater than 0
+ * @param attachBehaviors if true, attach ViewPlatformBehaviors to the
+ * appropriate ViewingPlatforms
+ */
+ ConfigContainer(URL userConfig, boolean setVisible,
+ int transformCount, boolean attachBehaviors) {
+
+ if (transformCount < 1)
+ throw new IllegalArgumentException
+ ("transformCount must be greater than 0") ;
+
+ loadConfig(userConfig) ;
+ processConfig(setVisible, transformCount, attachBehaviors) ;
+ }
+
+ /**
+ * Open, parse, and load the contents of a configuration file.
+ *
+ * @param userConfig location of the configuration file
+ */
+ private void loadConfig(URL userConfig) {
+ InputStream inputStream = null ;
+ StreamTokenizer streamTokenizer = null ;
+ String lastFileName = currentFileName ;
+
+ currentFileName = userConfig.toString() ;
+ try {
+ inputStream = userConfig.openStream() ;
+ Reader r = new BufferedReader(new InputStreamReader(inputStream)) ;
+ streamTokenizer = new StreamTokenizer(r) ;
+ }
+ catch (IOException e) {
+ throw new IllegalArgumentException
+ ("\n" + e + "\nUnable to open " + currentFileName) ;
+ }
+
+ //
+ // Set up syntax tables for the tokenizer.
+ //
+ // It would be nice to allow '/' as a word constituent for URL strings
+ // and Unix paths, but then the scanner won't ignore "//" and "/* */"
+ // comment style syntax. Treating '/' as an ordinary character will
+ // allow comments to work, but then '/' becomes a single token which
+ // has to be concatenated with subsequent tokens to reconstruct the
+ // original word string.
+ //
+ // It is cleaner to just require quoting for forward slashes. '/'
+ // should still be treated as an ordinary character however, so that a
+ // non-quoted URL string or Unix path will be treated as a syntax
+ // error instead of a comment.
+ //
+ streamTokenizer.ordinaryChar('/') ;
+ streamTokenizer.wordChars('_', '_') ;
+ streamTokenizer.wordChars('$', '$') ; // for ${...} Java property
+ streamTokenizer.wordChars('{', '}') ; // substitution in word tokens
+ streamTokenizer.slashSlashComments(true) ;
+ streamTokenizer.slashStarComments(true) ;
+
+ // Create an s-expression parser to use for all top-level (0) commands.
+ ConfigSexpression sexp = new ConfigSexpression() ;
+
+ // Loop through all top-level commands. Boolean.FALSE is returned
+ // after the last one is evaluated.
+ while (sexp.parseAndEval(this, streamTokenizer, 0) != Boolean.FALSE) ;
+
+ // Close the input stream.
+ try {
+ inputStream.close() ;
+ }
+ catch (IOException e) {
+ throw new IllegalArgumentException
+ ("\n" + e + "\nUnable to close " + currentFileName) ;
+ }
+
+ // Restore current file name.
+ currentFileName = lastFileName ;
+ }
+
+ /**
+ * This method gets called from the s-expression parser to process a
+ * configuration command.
+ *
+ * @param elements tokenized list of sexp elements
+ * @param lineNumber command line number
+ */
+ void evaluateCommand(ArrayList elements, int lineNumber) {
+ ConfigObject co ;
+ ConfigCommand cmd ;
+
+ // Create a command object.
+ cmd = new ConfigCommand(elements, currentFileName, lineNumber) ;
+
+ // Process the command according to its type.
+ switch (cmd.type) {
+ case ConfigCommand.CREATE:
+ co = createConfigObject(cmd) ;
+ addConfigObject(co) ;
+ break ;
+ case ConfigCommand.ALIAS:
+ co = createConfigAlias(cmd) ;
+ addConfigObject(co) ;
+ break ;
+ case ConfigCommand.PROPERTY:
+ co = findConfigObject(cmd.baseName, cmd.instanceName) ;
+ co.setProperty(cmd) ;
+ break ;
+ case ConfigCommand.INCLUDE:
+ if (! (cmd.argv[1] instanceof String)) {
+ throw new IllegalArgumentException
+ ("Include file must be a URL string") ;
+ }
+ URL url = null ;
+ String urlString = (String)cmd.argv[1] ;
+ try {
+ url = new URL(urlString) ;
+ }
+ catch (MalformedURLException e) {
+ throw new IllegalArgumentException(e.toString()) ;
+ }
+ loadConfig(url) ;
+ break ;
+ case ConfigCommand.IGNORE:
+ break ;
+ default:
+ throw new IllegalArgumentException
+ ("Unknown command \"" + cmd.commandName + "\"") ;
+ }
+ }
+
+ /**
+ * Instantiates and initializes an object that extends the ConfigObject
+ * base class. The class name of the object is derived from the
+ * command, which is of the following form:<p>
+ *
+ * (New{baseName} {instanceName} ... [Alias {aliasName}])<p>
+ *
+ * The first two command elements and the optional trailing Alias syntax
+ * are processed here, at which point the subclass implementation of
+ * initialize() is called. Subclasses must override initialize() if they
+ * need to process more than what is processed by default here.
+ *
+ * @param cmd configuration command that creates a new ConfigObject
+ */
+ private ConfigObject createConfigObject(ConfigCommand cmd) {
+ Class objectClass = null ;
+ ConfigObject configObject = null ;
+
+ // Instantatiate the ConfigObject if possible. This is not the target
+ // object, but an object that will gather configuration properties,
+ // instantiate the target object, and then apply the configuration
+ // properties to it.
+ try {
+ objectClass = Class.forName("com.sun.j3d.utils.universe.Config" +
+ cmd.baseName) ;
+ }
+ catch (ClassNotFoundException e) {
+ throw new IllegalArgumentException
+ ("\"" + cmd.baseName + "\"" +
+ " is not a configurable object; ignoring command") ;
+ }
+ try {
+ configObject = (ConfigObject)(objectClass.newInstance()) ;
+ }
+ catch (IllegalAccessException e) {
+ System.out.println(e) ;
+ throw new IllegalArgumentException("Ignoring command") ;
+ }
+ catch (InstantiationException e) {
+ System.out.println(e) ;
+ throw new IllegalArgumentException("Ignoring command") ;
+ }
+
+ // Process an Alias keyword if present. This option is available for
+ // all New commands so it is processed here. The Alias keyword must
+ // be the penultimate command element, followed by a String.
+ for (int i = 2 ; i < cmd.argc ; i++) {
+ if (cmd.argv[i] instanceof String &&
+ ((String)cmd.argv[i]).equals("Alias")) {
+ if (i == (cmd.argc - 2) && cmd.argv[i+1] instanceof String) {
+ addConfigObject(new ConfigAlias(cmd.baseName,
+ (String)cmd.argv[i+1],
+ configObject)) ;
+ cmd.argc -= 2 ;
+ }
+ else {
+ throw new IllegalArgumentException
+ ("The alias name must be a string and " +
+ "must be the last command argument") ;
+ }
+ }
+ }
+
+ // Initialize common fields.
+ configObject.baseName = cmd.baseName ;
+ configObject.instanceName = cmd.instanceName ;
+ configObject.creatingCommand = cmd ;
+ configObject.configContainer = this ;
+
+ // Initialize specific fields and return the ConfigObject.
+ configObject.initialize(cmd) ;
+ return configObject ;
+ }
+
+ /**
+ * Instantiate and initialize a ConfigObject base class containing alias
+ * information. The command is of the form:<p>
+ *
+ * ({baseName}Alias {aliasName} {originalName})
+ *
+ * @param cmd configuration command that creates a new alias
+ * @return the new ConfigObject with alias information
+ */
+ private ConfigObject createConfigAlias(ConfigCommand cmd) {
+ ConfigObject original ;
+
+ if (cmd.argc != 3 || ! (cmd.argv[2] instanceof String))
+ throw new IllegalArgumentException
+ ("Command \"" + cmd.commandName +
+ "\" requires an instance name as second argument") ;
+
+ original = findConfigObject(cmd.baseName, (String)cmd.argv[2]) ;
+ return new ConfigAlias(cmd.baseName, cmd.instanceName, original) ;
+ }
+
+ /**
+ * A class that does nothing but reference another ConfigObject. Once
+ * created, the alias name can be used in all commands that would accept
+ * the original name. A lookup of the alias name will always return the
+ * original instance.
+ */
+ private static class ConfigAlias extends ConfigObject {
+ ConfigAlias(String baseName, String instanceName, ConfigObject targ) {
+ this.baseName = baseName ;
+ this.instanceName = instanceName ;
+ this.isAlias = true ;
+ this.original = targ ;
+ targ.aliases.add(instanceName) ;
+ }
+ }
+
+ /**
+ * Adds the specified ConfigObject instance into this container using the
+ * given ConfigCommand's base name and instance name.
+ *
+ * @param object the ConfigObject instance to add into the database
+ */
+ private void addConfigObject(ConfigObject object) {
+ ArrayList instances ;
+
+ instances = (ArrayList)baseNameMap.get(object.baseName) ;
+ if (instances == null) {
+ instances = new ArrayList() ;
+ baseNameMap.put(object.baseName, instances) ;
+ }
+
+ // Disallow duplicate instance names.
+ for (int i = 0 ; i < instances.size() ; i++) {
+ ConfigObject co = (ConfigObject)instances.get(i) ;
+ if (co.instanceName.equals(object.instanceName)) {
+ // Don't confuse anybody using Window.
+ String base = object.baseName ;
+ if (base.equals("Screen")) base = "Screen or Window" ;
+ throw new IllegalArgumentException
+ ("Duplicate " + base + " instance name \"" +
+ object.instanceName + "\" ignored") ;
+ }
+ }
+
+ instances.add(object) ;
+ }
+
+ /**
+ * Finds a config object matching the given base name and the instance
+ * name. If an alias is found, then its original is returned. If the
+ * object is not found, an IllegalArgumentException is thrown.<p>
+ *
+ * @param basename base name of the config object
+ * @param instanceName name associated with this config object instance
+ * @return the found ConfigObject
+ */
+ ConfigObject findConfigObject(String baseName, String instanceName) {
+ ArrayList instances ;
+ ConfigObject configObject ;
+
+ instances = (ArrayList)baseNameMap.get(baseName) ;
+ if (instances != null) {
+ for (int i = 0 ; i < instances.size() ; i++) {
+ configObject = (ConfigObject)instances.get(i) ;
+
+ if (configObject.instanceName.equals(instanceName)) {
+ if (configObject.isAlias)
+ return configObject.original ;
+ else
+ return configObject ;
+ }
+ }
+ }
+
+ // Throw an error, but don't confuse anybody using Window.
+ if (baseName.equals("Screen")) baseName = "Screen or Window" ;
+ throw new IllegalArgumentException
+ (baseName + " \"" + instanceName + "\" not found") ;
+ }
+
+ /**
+ * Find instances of config objects with the given base name.
+ * This is the same as <code>findConfigObjects(baseName, true)</code>.
+ * Aliases are filtered out so that all returned instances are unique.
+ *
+ * @param baseName base name of desired config object class
+ * @return ArrayList of config object instances of the desired base
+ * class, or null if instances of the base class don't exist
+ */
+ Collection findConfigObjects(String baseName) {
+ return findConfigObjects(baseName, true) ;
+ }
+
+
+ /**
+ * Find instances of config objects with the given base name.
+ *
+ * @param baseName base name of desired config object class
+ * @param filterAlias if true, aliases are filtered out so that all
+ * returned instances are unique
+ * @return ArrayList of config object instances of the desired base
+ * class, or null if instances of the base class don't exist
+ */
+ Collection findConfigObjects(String baseName, boolean filterAlias) {
+ ArrayList instances ;
+
+ instances = (ArrayList)baseNameMap.get(baseName) ;
+ if (instances == null || instances.size() == 0) {
+ return null ; // This is not an error.
+ }
+
+ if (filterAlias) {
+ ArrayList output = new ArrayList() ;
+ for (int i = 0 ; i < instances.size() ; i++) {
+ ConfigObject configObject = (ConfigObject)instances.get(i) ;
+
+ if (! configObject.isAlias) {
+ output.add(configObject) ;
+ }
+ }
+ return output ;
+ }
+ else {
+ return instances ;
+ }
+ }
+
+ /**
+ * Returns the ConfigObject associated with the name in the given
+ * ConfigCommand. This is used for evaluating retained built-in commands
+ * after the config file has already been parsed. The parser won't catch
+ * any of the exceptions generated by this method, so the error messages
+ * are wrapped accordingly.
+ *
+ * @param basename base name of the config object
+ * @param cmd command containing the name in argv[1]
+ * @return the found ConfigObject
+ */
+ private ConfigObject findConfigObject(String baseName, ConfigCommand cmd) {
+ if (cmd.argc != 2 || !(cmd.argv[1] instanceof String))
+ throw new IllegalArgumentException
+ (ConfigObject.errorMessage
+ (cmd, "Parameter must be a single string")) ;
+ try {
+ return findConfigObject(baseName, (String)cmd.argv[1]) ;
+ }
+ catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException
+ (ConfigObject.errorMessage(cmd, e.getMessage())) ;
+ }
+ }
+
+ /**
+ * This method gets called from a ConfigObject to evaluate a retained
+ * built-in command nested within a property command. These are commands
+ * that can't be evaluated until the entire config file is parsed.
+ *
+ * @param cmd the built-in command
+ * @return object representing result of evaluation
+ */
+ Object evaluateBuiltIn(ConfigCommand cmd) {
+ int argc = cmd.argc ;
+ Object[] argv = cmd.argv ;
+
+ if (cmd.commandName.equals("ConfigContainer")) {
+ // return a reference to this ConfigContainer
+ return this ;
+ }
+ else if (cmd.commandName.equals("Canvas3D")) {
+ // Look for canvases in the screen database.
+ return ((ConfigScreen)findConfigObject("Screen", cmd)).j3dCanvas ;
+ }
+ else if (baseNameMap.get(cmd.commandName) != null) {
+ // Handle commands of the form ({objectType} name) that return the
+ // object associated with the name.
+ return findConfigObject(cmd.commandName, cmd).targetObject ;
+ }
+ else {
+ // So far no other retained built-in commands.
+ throw new IllegalArgumentException
+ (ConfigObject.errorMessage(cmd, "Unknown built-in command \"" +
+ cmd.commandName + "\"")) ;
+ }
+ }
+
+ /**
+ * Process the configuration after parsing the configuration file.
+ * Note: the processing order of the various config objects is
+ * significant.
+ *
+ * @param setVisible true if Viewer components should be visible
+ * @param transformCount number of TransformGroups with which
+ * ViewingPlatforms should be created
+ * @param attachBehaviors true if behaviors should be attached to
+ * ViewingPlatforms
+ */
+ private void processConfig(boolean setVisible,
+ int transformCount, boolean attachBehaviors) {
+
+ Collection c, s, pe, vp ;
+ this.setVisible = setVisible ;
+ this.transformCount = transformCount ;
+
+ c = findConfigObjects("PhysicalBody") ;
+ if (c != null) {
+ processPhysicalBodies(c) ;
+ }
+
+ pe = findConfigObjects("PhysicalEnvironment") ;
+ if (pe != null) {
+ processPhysicalEnvironments(pe) ;
+ }
+
+ c = findConfigObjects("View") ;
+ if (c != null) {
+ processViews(c, setVisible) ;
+ }
+
+ c = findConfigObjects("Device") ;
+ s = findConfigObjects("Sensor") ;
+ if (c != null) {
+ processDevices(c, s, pe) ;
+ }
+
+ vp = findConfigObjects("ViewPlatform") ;
+ if (vp != null) {
+ processViewPlatforms(vp, transformCount) ;
+ }
+
+ c = findConfigObjects("ViewPlatformBehavior") ;
+ if (c != null) {
+ processViewPlatformBehaviors(c, vp, attachBehaviors) ;
+ }
+
+ c = findConfigObjects("Object") ;
+ if (c != null) {
+ processGenericObjects(c) ;
+ }
+ }
+
+ // Process config physical environments into Java 3D physical
+ // environments.
+ private void processPhysicalEnvironments(Collection c) {
+ Iterator i = c.iterator() ;
+ while (i.hasNext()) {
+ ConfigPhysicalEnvironment e = (ConfigPhysicalEnvironment)i.next() ;
+ e.targetObject = e.createJ3dPhysicalEnvironment() ;
+ }
+ }
+
+ // Process config physical bodys into Java 3D physical bodies.
+ private void processPhysicalBodies(Collection c) {
+ Iterator i = c.iterator() ;
+ while (i.hasNext()) {
+ ConfigPhysicalBody b = (ConfigPhysicalBody)i.next() ;
+ b.targetObject = b.createJ3dPhysicalBody() ;
+ }
+ }
+
+ // Process config views into Java 3D Views and then create Viewer objects
+ // for them. This should only be called after all physical bodies and
+ // physical environments have been processed.
+ private void processViews(Collection c, boolean setVisible) {
+ Iterator i = c.iterator() ;
+ while (i.hasNext()) {
+ ConfigView v = (ConfigView)i.next() ;
+ v.targetObject = v.createViewer(setVisible) ;
+ }
+ }
+
+ // Process config devices into Java 3D input devices. This should be done
+ // only after all views have been processed, as some InputDevice
+ // implementations require the AWT components associated with a view.
+ private void processDevices(Collection c, Collection s, Collection p) {
+ ConfigDevice cd = null ;
+ Iterator i = c.iterator() ;
+ while (i.hasNext()) {
+ cd = (ConfigDevice)i.next() ;
+ cd.targetObject = cd.createInputDevice() ;
+ }
+
+ // Process device properties only after all InputDevices have been
+ // instantiated. Some InputDevice properties require references
+ // to other InputDevice implementations.
+ i = c.iterator() ;
+ while (i.hasNext()) ((ConfigDevice)i.next()).processProperties() ;
+
+ // Initialize the devices only after all have been instantiated, as
+ // some InputDevices implementations are slaved to the first one
+ // created and will not initialize otherwise (e.g. LogitechTracker).
+ i = c.iterator() ;
+ while (i.hasNext()) {
+ cd = (ConfigDevice)i.next() ;
+ if (! cd.j3dInputDevice.initialize())
+ throw new RuntimeException
+ (cd.errorMessage(cd.creatingCommand,
+ "could not initialize device \"" +
+ cd.instanceName + "\"")) ;
+ }
+
+ // An InputDevice implementation will have created all its Sensors by
+ // the time initialize() returns. Retrieve and configure them here.
+ if (s != null) {
+ i = s.iterator() ;
+ while (i.hasNext()) {
+ ConfigSensor cs = (ConfigSensor)i.next() ;
+ cs.configureSensor() ;
+ cs.targetObject = cs.j3dSensor ;
+ }
+ }
+
+ // Iterate through the PhysicalEnvironments and process the devices.
+ if (p != null) {
+ i = p.iterator() ;
+ while (i.hasNext())
+ ((ConfigPhysicalEnvironment)i.next()).processDevices() ;
+ }
+ }
+
+ // Process config view platforms into Java 3D viewing platforms.
+ private void processViewPlatforms(Collection c, int numTransforms) {
+ Iterator i = c.iterator() ;
+ while (i.hasNext()) {
+ ConfigViewPlatform cvp = (ConfigViewPlatform)i.next() ;
+ cvp.targetObject = cvp.createViewingPlatform(numTransforms) ;
+ }
+ }
+
+ // Process the configured view platform behaviors.
+ private void processViewPlatformBehaviors(Collection behaviors,
+ Collection viewPlatforms,
+ boolean attach) {
+ Iterator i = behaviors.iterator() ;
+ while (i.hasNext()) {
+ ConfigViewPlatformBehavior b =
+ (ConfigViewPlatformBehavior)i.next() ;
+ b.targetObject = b.createViewPlatformBehavior() ;
+ }
+
+ // Process properties only after all behaviors are instantiated.
+ i = behaviors.iterator() ;
+ while (i.hasNext())
+ ((ConfigViewPlatformBehavior)i.next()).processProperties() ;
+
+ // Attach behaviors to platforms after properties processed.
+ if (attach && viewPlatforms != null) {
+ i = viewPlatforms.iterator() ;
+ while (i.hasNext())
+ ((ConfigViewPlatform)i.next()).processBehavior() ;
+ }
+ }
+
+ // Process generic objects.
+ private void processGenericObjects(Collection objects) {
+ Iterator i = objects.iterator() ;
+ while (i.hasNext()) {
+ ConfigObject o = (ConfigObject)i.next() ;
+ o.targetObject = o.createTargetObject() ;
+ }
+
+ // Process properties only after all target objects are instantiated.
+ i = objects.iterator() ;
+ while (i.hasNext()) ((ConfigObject)i.next()).processProperties() ;
+ }
+
+ // Returns a read-only Set containing all unique Java 3D objects of the
+ // specified base class.
+ private ReadOnlySet createSet(String baseName) {
+ Collection c = findConfigObjects(baseName, true) ;
+ if (c == null || c.size() == 0)
+ return null ;
+
+ Iterator i = c.iterator() ;
+ ArrayList l = new ArrayList() ;
+ while (i.hasNext()) l.add(((ConfigObject)i.next()).targetObject) ;
+
+ return new ReadOnlySet(l) ;
+ }
+
+ // Returns a read-only Map that maps all names in the specified base
+ // class, including aliases, to their corresponding Java 3D objects.
+ private ReadOnlyMap createMap(String baseName) {
+ Collection c = findConfigObjects(baseName, false) ;
+ if (c == null || c.size() == 0)
+ return null ;
+
+ Iterator i = c.iterator() ;
+ HashMap m = new HashMap() ;
+ while (i.hasNext()) {
+ ConfigObject co = (ConfigObject)i.next() ;
+ if (co.isAlias)
+ m.put(co.instanceName, co.original.targetObject) ;
+ else
+ m.put(co.instanceName, co.targetObject) ;
+ }
+
+ return new ReadOnlyMap(m) ;
+ }
+
+ /**
+ * Returns a read-only Set of all configured PhysicalBody instances in the
+ * order they were defined in the configuration file.
+ *
+ * PhysicalBody instances are created with the following command:<p>
+ * <blockquote>
+ * (NewPhysicalBody <i>&lt;instance name&gt;</i>
+ * [Alias <i>&lt;alias name&gt;</i>])
+ * </blockquote>
+ *
+ * The PhysicalBody is configured through the following command:<p>
+ * <blockquote>
+ * (PhysicalBodyProperty <i>&lt;instance name&gt;
+ * &lt;property name&gt; &lt;property value&gt;</i>)
+ * </blockquote>
+ *
+ * @return read-only Set of all unique instances, or null
+ */
+ public Set getPhysicalBodies() {
+ if (bodies != null) return bodies ;
+ bodies = createSet("PhysicalBody") ;
+ return bodies ;
+ }
+
+ /**
+ * Returns a read-only Map that maps PhysicalBody names to instances.
+ * Names may be aliases and if so will map to the original instances.
+ *
+ * @return read-only Map from names to PhysicalBody instances, or null if
+ * no instances
+ */
+ public Map getNamedPhysicalBodies() {
+ if (bodyMap != null) return bodyMap ;
+ bodyMap = createMap("PhysicalBody") ;
+ return bodyMap ;
+ }
+
+ /**
+ * Returns a read-only Set of all configured PhysicalEnvironment instances
+ * in the order they were defined in the configuration file.<p>
+ *
+ * PhysicalEnvironment instances are created with the following command:<p>
+ * <blockquote>
+ * (NewPhysicalEnvironment <i>&lt;instance name&gt;</i>
+ * [Alias <i>&lt;alias name&gt;</i>])
+ * </blockquote>
+ *
+ * The PhysicalEnvironment is configured through the following command:<p>
+ * <blockquote>
+ * (PhysicalEnvironmentProperty <i>&lt;instance name&gt;
+ * &lt;property name&gt; &lt;property value&gt;</i>)
+ * </blockquote>
+ *
+ * @return read-only Set of all unique instances, or null
+ */
+ public Set getPhysicalEnvironments() {
+ if (environments != null) return environments ;
+ environments = createSet("PhysicalEnvironment") ;
+ return environments ;
+ }
+
+ /**
+ * Returns a read-only Map that maps PhysicalEnvironment names to
+ * instances. Names may be aliases and if so will map to the original
+ * instances.
+ *
+ * @return read-only Map from names to PhysicalEnvironment instances, or
+ * null if no instances
+ */
+ public Map getNamedPhysicalEnvironments() {
+ if (environmentMap != null) return environmentMap ;
+ environmentMap = createMap("PhysicalEnvironment") ;
+ return environmentMap ;
+ }
+
+ /**
+ * Returns a read-only Set of all configured Viewer instances in the order
+ * they were defined in the configuration file. The Viewers will have
+ * incorporated any PhysicalEnvironment and PhysicalBody objects specfied
+ * for them in the configuration file, and will be attached to any
+ * ViewingPlatforms specified for them.<p>
+ *
+ * Viewer instances are created with the following command:<p>
+ * <blockquote>
+ * (NewView <i>&lt;instance name&gt;</i> [Alias <i>&lt;alias name&gt;</i>])
+ * </blockquote>
+ *
+ * The Viewer is configured through the following command:<p>
+ * <blockquote>
+ * (ViewProperty <i>&lt;instance name&gt;
+ * &lt;property name&gt; &lt;property value&gt;</i>)
+ * </blockquote>
+ *
+ * @return read-only Set of all unique instances, or null
+ */
+ public Set getViewers() {
+ if (viewers != null) return viewers ;
+ viewers = createSet("View") ;
+ return viewers ;
+ }
+
+ /**
+ * Returns a read-only Map that maps Viewer names to instances.
+ * Names may be aliases and if so will map to the original instances.
+ * The Viewers will have incorporated any PhysicalEnvironment and
+ * PhysicalBody objects specfied for them in the configuration file, and
+ * will be attached to any ViewingPlatforms specified for them.<p>
+ *
+ * @return read-only Map from names to Viewer instances, or
+ * null if no instances
+ */
+ public Map getNamedViewers() {
+ if (viewerMap != null) return viewerMap ;
+ viewerMap = createMap("View") ;
+ return viewerMap ;
+ }
+
+ /**
+ * Returns a read-only Set of all configured InputDevice instances in the
+ * order they were defined in the configuration file. All InputDevice
+ * instances in the set are initialized and registered with any
+ * PhysicalEnvironments that reference them.<p>
+ *
+ * InputDevice instances are created with the following command:<p>
+ * <blockquote>
+ * (NewDevice <i>&lt;instanceName&gt; &lt;className&gt;</i>
+ * [Alias <i>&lt;alias name&gt;</i>])
+ * </blockquote>
+ *
+ * <i>className</i> must be the fully-qualified name of a class that
+ * implements the InputDevice interface. The implementation
+ * must provide a parameterless constructor.<p>
+ *
+ * The InputDevice is configured through the DeviceProperty command:<p>
+ * <blockquote>
+ * (DeviceProperty <i>&lt;instanceName&gt; &lt;propertyName&gt;
+ * &lt;arg0&gt; ... &lt;argn&gt;</i>)
+ * </blockquote>
+ *
+ * <i>propertyName</i> must be the name of a input device method that
+ * takes an array of Objects as its only parameter; the array is populated
+ * with the values of <i>arg0</i> through <i>argn</i> when the method is
+ * invoked to set the property. These additional requirements for
+ * configurable input devices can usually be fulfilled by extending or
+ * wrapping available InputDevice implementations.
+ *
+ * @return read-only Set of all unique instances, or null
+ */
+ public Set getInputDevices() {
+ if (devices != null) return devices ;
+ devices = createSet("Device") ;
+ return devices ;
+ }
+
+ /**
+ * Returns a read-only Map that maps InputDevice names to instances.
+ * Names may be aliases and if so will map to the original instances. All
+ * InputDevice instances in the map are initialized and registered with
+ * any PhysicalEnvironments that reference them.
+ *
+ * @return read-only Map from names to InputDevice instances, or
+ * null if no instances
+ * @see #getInputDevices
+ */
+ public Map getNamedInputDevices() {
+ if (deviceMap != null) return deviceMap ;
+ deviceMap = createMap("Device") ;
+ return deviceMap ;
+ }
+
+ /**
+ * Returns a read-only Set of all configured Sensor instances in the order
+ * they were defined in the configuration file. The associated
+ * InputDevices are all initialized and registered with any
+ * PhysicalEnvironments that reference them.<p>
+ *
+ * Sensor instances are named with the following command:<p>
+ * <blockquote>
+ * (NewSensor <i>&lt;instance name&gt; &lt;device name&gt;
+ * &lt;sensor index&gt;</i> [Alias <i>&lt;alias name&gt;</i>])
+ * </blockquote>
+ *
+ * <i>device name</i> is the instance name of a previously defined
+ * InputDevice, and <i>sensor index</i> is the index of the Sensor to be
+ * bound to <i>instance name</i>. The InputDevice implementation is
+ * responsible for creating its own Sensor objects, so this command does
+ * not create any new instances.<p>
+ *
+ * The Sensor is configured through the SensorProperty command:<p>
+ * <blockquote>
+ * (SensorProperty <i>&lt;instance name&gt; &lt;property name&gt;
+ * &lt;property value&gt;</i>)
+ * </blockquote>
+ *
+ * With the sole exception of the Sensor assigned to the head tracker,
+ * none of the Sensors defined in the configuration file are placed into
+ * the Sensor array maintained by a PhysicalEnvironment.
+ *
+ * @return read-only Set of all unique instances, or null
+ */
+ public Set getSensors() {
+ if (sensors != null) return sensors ;
+ sensors = createSet("Sensor") ;
+ return sensors ;
+ }
+
+ /**
+ * Returns a read-only Map that maps Sensor names to instances. Names may
+ * be aliases and if so will map to the original instances. The
+ * associated InputDevices are all initialized and registered with any
+ * PhysicalEnvironments that reference them.<p>
+ *
+ * With the sole exception of the Sensor assigned to the head tracker,
+ * none of the Sensors defined in the configuration file are placed into
+ * the Sensor array maintained by a PhysicalEnvironment.
+ *
+ * @return read-only Map from names to Sensor instances, or
+ * null if no instances
+ */
+ public Map getNamedSensors() {
+ if (sensorMap != null) return sensorMap ;
+ sensorMap = createMap("Sensor") ;
+ return sensorMap ;
+ }
+
+ /**
+ * Returns a read-only Set of all configured ViewingPlatform instances in
+ * the order they were defined in the configuration file. The
+ * ConfigContainer class itself does not attach the ViewingPlatform
+ * instances to any scengraph components or universe Locales; they are not
+ * "live" until made so by a separate client such as ConfiguredUniverse.
+ *
+ * ViewingPlatform instances are created with the following command:<p>
+ * <blockquote>
+ * (NewViewPlatform <i>&lt;instance name&gt;</i>
+ * [Alias <i>&lt;alias name&gt;</i>])
+ * </blockquote>
+ *
+ * The ViewingPlatform is configured through the following command:<p>
+ * <blockquote>
+ * (ViewPlatformProperty <i>&lt;instance name&gt; &lt;property name&gt;
+ * &lt;property value&gt;</i>)
+ * </blockquote>
+ *
+ * @return read-only Set of all unique instances, or null
+ */
+ public Set getViewingPlatforms() {
+ if (platforms != null) return platforms ;
+ platforms = createSet("ViewPlatform") ;
+ return platforms ;
+ }
+
+ /**
+ * Returns a read-only Map that maps ViewingPlatform names to instances.
+ * Names may be aliases and if so will map to the original instances. The
+ * ConfigContainer class itself does not attach the ViewingPlatform
+ * instances to any scengraph components or universe Locales; they are not
+ * "live" until made so by a separate client such as ConfiguredUniverse.
+ *
+ * @return read-only Map from names to ViewingPlatform instances, or
+ * null if no instances
+ */
+ public Map getNamedViewingPlatforms() {
+ if (platformMap != null) return platformMap ;
+ platformMap = createMap("ViewPlatform") ;
+ return platformMap ;
+ }
+
+ /**
+ * Returns a read-only Set of all configured ViewPlatformBehavior
+ * instances in the order they were defined in the configuration file.<p>
+ *
+ * The behaviors are attached to any ViewingPlatforms that specified them;
+ * that is, the <code>setViewPlatformBehavior</code> and
+ * <code>setViewingPlatform</code> methods of ViewingPlatform and
+ * ViewPlatformBehavior have been called if appropriate. However, a
+ * behavior's <code>initialize</code> method is not called until the
+ * ViewingPlatform to which it is attached is made live.<p>
+ *
+ * ViewPlatformBehavior instances are created by the following command:<p>
+ * <blockquote>
+ * (NewViewPlatformBehavior <i>&lt;instanceName&gt; &lt;className&gt;</i>)
+ * </blockquote>
+ *
+ * <i>className</i> must be the fully qualified name of a concrete class
+ * that extends the abstract ViewPlatformBehavior class. The
+ * implementation must provide a parameterless constructor.<p>
+ *
+ * The behavior is configured using ViewPlatformBehaviorProperty:<p>
+ * <blockquote>
+ * (ViewPlatformBehaviorProperty <i>&lt;instanceName&gt;
+ * &lt;propertyName&gt; &lt;arg0&gt; ... &lt;argn&gt;</i>)
+ * </blockquote>
+ *
+ * ViewPlatformBehavior subclasses inherit a number of pre-defined
+ * properties that can be directly specified with the <i>propertyName</i>
+ * string; see the configuration file documentation for details.<p>
+ *
+ * Concrete ViewPlatformBehavior instances can also define their own
+ * unique properties. In those cases, <i>propertyName</i> must be the
+ * name of a behavior method that takes an array of Objects as its only
+ * parameter; the array is populated with the values of <i>arg0</i>
+ * through <i>argn</i> when the method is invoked to set the property.
+ * These additional requirements for configurable behaviors can usually be
+ * fulfilled by extending or wrapping available ViewPlatformBehavior
+ * subclasses.
+ *
+ * @return read-only Set of all unique instances, or null
+ */
+ public Set getViewPlatformBehaviors() {
+ if (behaviors != null) return behaviors ;
+ behaviors = createSet("ViewPlatformBehavior") ;
+ return behaviors ;
+ }
+
+ /**
+ * Returns a read-only Map that maps ViewPlatformBehavior names to
+ * instances. Names may be aliases and if so will map to the original
+ * instances.<p>
+ *
+ * The behaviors are attached to any ViewingPlatforms that specified them;
+ * that is, the <code>setViewPlatformBehavior</code> and
+ * <code>setViewingPlatform</code> methods of ViewingPlatform and
+ * ViewPlatformBehavior have been called if appropriate. However, a
+ * behavior's <code>initialize</code> method is not called until the
+ * ViewingPlatform to which it is attached is made live.<p>
+ *
+ * @return read-only Map from names to ViewPlatformBehavior instances, or
+ * null if no instances
+ * @see #getViewPlatformBehaviors
+ */
+ public Map getNamedViewPlatformBehaviors() {
+ if (behaviorMap != null) return behaviorMap ;
+ behaviorMap = createMap("ViewPlatformBehavior") ;
+ return behaviorMap ;
+ }
+
+ /**
+ * Returns a read-only Map containing the named Canvas3D instances used by
+ * the specified Viewer. Names may be aliases and if so will map to the
+ * original instances. The set of unique Canvas3D instances used by a
+ * Viewer may be obtained by calling the Viewer's accessor methods
+ * directly.<p>
+ *
+ * A named Canvas3D is created and added to a Viewer whenever any of the
+ * following configuration commands are used:<p>
+ * <blockquote>
+ * (ViewProperty <i>&lt;view&gt;</i> Screen <i>&lt;screenName&gt;</i>)<br>
+ * (ViewProperty <i>&lt;view&gt;</i> Window <i>&lt;windowName&gt;</i>)
+ * </blockquote>
+ *
+ * <i>view</i> is the name of a Viewer created with the NewView command.
+ * The <i>screenName</i> and <i>windowName</i> parameters of the above
+ * commands are the keys to use when looking up the associated Canvas3D
+ * instances in the Map returned by this method. <b>Note:</b> the
+ * NewScreen and NewWindow commands do <i>not</i> create Canvas3D
+ * instances themselves; they are created only by the above configuration
+ * commands.
+ *
+ * @param viewName the name of the Viewer
+ * @return read-only Map containing the Viewer's named Canvas3D instances
+ */
+ public Map getNamedCanvases(String viewName) {
+ Map m = (Map)viewCanvasMap.get(viewName) ;
+ if (m != null) return m ;
+
+ m = new HashMap() ;
+ ConfigView cv = (ConfigView)findConfigObject("View", viewName) ;
+ Iterator i = cv.screens.iterator() ;
+ while (i.hasNext()) {
+ ConfigScreen cs = (ConfigScreen)i.next() ;
+ m.put(cs.instanceName, cs.j3dCanvas) ;
+
+ // The aliases list contains all alias strings for the canvas.
+ Iterator j = cs.aliases.iterator() ;
+ while (j.hasNext()) m.put(j.next(), cs.j3dCanvas) ;
+ }
+ m = new ReadOnlyMap(m) ;
+ viewCanvasMap.put(viewName, m) ;
+ return m ;
+ }
+
+ /**
+ * Returns a read-only Set of all generic configuration object
+ * instances in the order they were defined in the configuration file.<p>
+ *
+ * Generic object instances are created with the following command:<p>
+ * <blockquote>
+ * (NewObject <i>&lt;instanceName&gt; &lt;className&gt;</i>)
+ * </blockquote>
+ *
+ * <i>className</i> must be the fully-qualified name of a class that
+ * provides a parameterless constructor.<p>
+ *
+ * The object is configured through the ObjectProperty command:<p>
+ * <blockquote>
+ * (ObjectProperty <i>&lt;instanceName&gt; &lt;propertyName&gt;
+ * &lt;arg0&gt; ... &lt;argn&gt;</i>)
+ * </blockquote>
+ *
+ * <i>propertyName</i> must be the name of a method provided by object
+ * <i>instanceName</i>. It must take an array of Objects as its only
+ * parameter; the array is populated with the values of <i>arg0</i>
+ * through <i>argn</i> when the method is invoked to set the property.
+ * These additional requirements for configurable objects can usually be
+ * fulfilled by extending or wrapping available object classes.
+ *
+ * @return read-only Set of all unique instances, or null
+ */
+ public Set getGenericObjects() {
+ if (genericObjects != null) return genericObjects ;
+ genericObjects = createSet("Object") ;
+ return genericObjects ;
+ }
+
+ /**
+ * Returns a read-only Map that maps generic object names to
+ * instances. Names may be aliases and if so will map to the original
+ * instances.
+ *
+ * @return read-only Map from names to generic object instances, or
+ * null if no instances
+ * @see #getGenericObjects
+ */
+ public Map getNamedGenericObjects() {
+ if (genericObjectMap != null) return genericObjectMap ;
+ genericObjectMap = createMap("Object") ;
+ return genericObjectMap ;
+ }
+
+ /**
+ * Returns the number of TransformGroups with which ViewingPlatforms
+ * should be created. This is useful for clients that wish to provide a
+ * default ViewingPlatform if the configuration file doesn't specify one.
+ *
+ * @return the number of TransformGroups
+ */
+ public int getViewPlatformTransformCount() {
+ return transformCount ;
+ }
+
+ /**
+ * Returns whether Viewers should be created with their AWT components
+ * initially visible or invisible. This is useful for clients that wish
+ * to provide a default Viewer if the configuration file doesn't specify
+ * one.
+ *
+ * @return true if Viewer components should be initially visible; false
+ * otherwise
+ */
+ public boolean getViewerVisibility() {
+ return setVisible ;
+ }
+
+ /**
+ * Release memory references used by this ConfigContainer. All Sets and
+ * Maps obtained from this ConfigContainer are cleared.
+ */
+ public void clear() {
+ // Clear baseNameList.
+ Iterator i = baseNameMap.values().iterator() ;
+ while (i.hasNext()) ((Collection)i.next()).clear() ;
+ baseNameMap.clear() ;
+
+ // Clear viewCanvasMap.
+ i = viewCanvasMap.values().iterator() ;
+ while (i.hasNext()) ((ReadOnlyMap)i.next()).map.clear() ;
+ viewCanvasMap.clear() ;
+
+ // Release reference to file name.
+ currentFileName = null ;
+
+ // Clear and release sets.
+ if (bodies != null) {
+ bodies.collection.clear() ;
+ bodies = null ;
+ }
+ if (environments != null) {
+ environments.collection.clear() ;
+ environments = null ;
+ }
+ if (devices != null) {
+ devices.collection.clear() ;
+ devices = null ;
+ }
+ if (sensors != null) {
+ sensors.collection.clear() ;
+ sensors = null ;
+ }
+ if (behaviors != null) {
+ behaviors.collection.clear() ;
+ behaviors = null ;
+ }
+ if (platforms != null) {
+ platforms.collection.clear() ;
+ platforms = null ;
+ }
+ if (viewers != null) {
+ viewers.collection.clear() ;
+ viewers = null ;
+ }
+ if (genericObjects != null) {
+ genericObjects.collection.clear() ;
+ genericObjects = null ;
+ }
+
+ // Clear and release maps.
+ if (bodyMap != null) {
+ bodyMap.map.clear() ;
+ bodyMap = null ;
+ }
+ if (environmentMap != null) {
+ environmentMap.map.clear() ;
+ environmentMap = null ;
+ }
+ if (deviceMap != null) {
+ deviceMap.map.clear() ;
+ deviceMap = null ;
+ }
+ if (sensorMap != null) {
+ sensorMap.map.clear() ;
+ sensorMap = null ;
+ }
+ if (behaviorMap != null) {
+ behaviorMap.map.clear() ;
+ behaviorMap = null ;
+ }
+ if (platformMap != null) {
+ platformMap.map.clear() ;
+ platformMap = null ;
+ }
+ if (viewerMap != null) {
+ viewerMap.map.clear() ;
+ viewerMap = null ;
+ }
+ if (genericObjectMap != null) {
+ genericObjectMap.map.clear() ;
+ genericObjectMap = null ;
+ }
+
+ }
+
+ /**
+ * Returns the config file URL based on system properties. The current
+ * implementation of this method parses the j3d.configURL property as a
+ * URL string. For example, the following command line would specify that
+ * the config file is taken from the file "j3dconfig" in the current
+ * directory:
+ * <ul>
+ * <code>java -Dj3d.configURL=file:j3dconfig ...</code>
+ * </ul>
+ *
+ * @return the URL of the config file; null is returned if no valid
+ * URL is defined by the system properties
+ */
+ public static URL getConfigURL() {
+ return getConfigURL(null) ;
+ }
+
+ /**
+ * Returns the config file URL based on system properties. The current
+ * implementation of this method parses the j3d.configURL property as a
+ * URL string. For example, the following command line would specify that
+ * the config file is taken from the file "j3dconfig" in the current
+ * directory:
+ * <ul>
+ * <code>java -Dj3d.configURL=file:j3dconfig ...</code>
+ * </ul>
+ *
+ * @param defaultURLString the default string used to construct
+ * the URL if the appropriate system properties are not defined
+ * @return the URL of the config file; null is returned if no
+ * valid URL is defined either by the system properties or the
+ * default URL string
+ */
+ public static URL getConfigURL(String defaultURLString) {
+ URL url = null ;
+ String urlString = null ;
+ final String defProp = defaultURLString ;
+
+ urlString = (String)java.security.AccessController.doPrivileged
+ (new java.security.PrivilegedAction() {
+ public Object run() {
+ return System.getProperty("j3d.configURL", defProp) ;
+ }
+ }) ;
+
+ if (urlString == null) {
+ return null ;
+ }
+ try {
+ url = new URL(urlString) ;
+ }
+ catch(MalformedURLException e) {
+ System.out.println(e) ;
+ return null ;
+ }
+ return url ;
+ }
+
+ // A general purpose read-only Map backed by a HashMap.
+ private static class ReadOnlyMap extends AbstractMap {
+ HashMap map ;
+ private Set entrySet = null ;
+
+ ReadOnlyMap(Map map) {
+ this.map = new HashMap(map) ;
+ }
+
+ // overridden for efficiency
+ public Object get(Object key) {
+ return map.get(key) ;
+ }
+
+ // overridden for efficiency
+ public boolean containsKey(Object key) {
+ return map.containsKey(key) ;
+ }
+
+ // overridden for efficiency
+ public boolean containsValue(Object value) {
+ return map.containsValue(value) ;
+ }
+
+ public Set entrySet() {
+ if (entrySet == null)
+ entrySet = new ReadOnlySet(map.entrySet()) ;
+
+ return entrySet ;
+ }
+ }
+
+ // A general purpose read-only Set backed by a Collection containing
+ // unique objects.
+ private static class ReadOnlySet extends AbstractSet {
+ Collection collection = null ;
+
+ ReadOnlySet(Collection c) {
+ this.collection = c ;
+ }
+
+ public int size() {
+ return collection.size() ;
+ }
+
+ public Iterator iterator() {
+ return new ReadOnlyIterator(collection.iterator()) ;
+ }
+ }
+
+ // A general purpose read-only Iterator backed by another Iterator.
+ private static class ReadOnlyIterator implements Iterator {
+ private Iterator i ;
+
+ ReadOnlyIterator(Iterator i) {
+ this.i = i ;
+ }
+
+ public boolean hasNext() {
+ return i.hasNext() ;
+ }
+
+ public Object next() {
+ return i.next() ;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException() ;
+ }
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/universe/ConfigDevice.java b/src/classes/share/com/sun/j3d/utils/universe/ConfigDevice.java
new file mode 100644
index 0000000..35b53cd
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/ConfigDevice.java
@@ -0,0 +1,66 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.universe ;
+import javax.media.j3d.InputDevice ;
+
+/**
+ * Mostly empty now; ConfigObject provides all required methods.
+ */
+class ConfigDevice extends ConfigObject {
+ /**
+ * The corresponding Java 3D core InputDevice instance.
+ */
+ InputDevice j3dInputDevice ;
+
+ /**
+ * Instantiate an InputDevice of the given class name.
+ *
+ * @return the InputDevice, or null if error
+ */
+ InputDevice createInputDevice() {
+ j3dInputDevice = (InputDevice)createTargetObject() ;
+ return j3dInputDevice ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/universe/ConfigObject.java b/src/classes/share/com/sun/j3d/utils/universe/ConfigObject.java
new file mode 100644
index 0000000..6783253
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/ConfigObject.java
@@ -0,0 +1,371 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.universe ;
+
+import java.lang.reflect.* ;
+import java.util.List ;
+import java.util.ArrayList ;
+
+/**
+ * Base class for all configuration objects. A ConfigObject processes
+ * configuration parameters for a target object, which is instantiated after
+ * the configuration file is parsed. The ConfigObject then applies its
+ * configuration properties to the target object.<p>
+ *
+ * Generic base implementations are provided for the initialize(),
+ * setProperty(), and processProperties() methods. These implementations
+ * assume target objects that are unknown and thus instantiated via
+ * introspection. Property names are assumed to be method names that take an
+ * array of Objects as a parameter; they are invoked through introspection as
+ * well.<p>
+ *
+ * Most ConfigObjects target concrete Java 3D core classes, so these
+ * implementations are usually overridden to instantiate those objects and
+ * call their methods directly.
+ */
+class ConfigObject {
+ /**
+ * The base name of this object, derived from the configuration command
+ * which created it. This is constructed by stripping off the leading
+ * "New" prefix or the trailing "Attribute", "Property", or "Alias" suffix
+ * of the command name. The name of the ConfigObject subclass which
+ * handles the command is derived by adding "Config" as a prefix to the
+ * base name.
+ */
+ String baseName = null ;
+
+ /**
+ * The instance name of this object, as specified in the configuration
+ * file.
+ */
+ String instanceName = null ;
+
+ /**
+ * The corresponding target object which this ConfigObject is configuring.
+ */
+ Object targetObject = null ;
+
+ /**
+ * The name of the target class this object is configuring.
+ */
+ String targetClassName = null ;
+
+ /**
+ * The Class object for the target.
+ */
+ Class targetClass = null ;
+
+ /**
+ * Configurable properties gathered by this object, represented by the
+ * ConfigCommands that set them.
+ */
+ List properties = new ArrayList() ;
+
+ /**
+ * The ConfigContainer in which this ConfigObject is contained.
+ */
+ ConfigContainer configContainer = null ;
+
+ /**
+ * The command that created this class.
+ */
+ ConfigCommand creatingCommand = null ;
+
+ /**
+ * If true, this object is an alias to another.
+ */
+ boolean isAlias = false ;
+
+ /**
+ * If isAlias is true, this references the original object.
+ */
+ ConfigObject original = null ;
+
+ /**
+ * List of alias Strings for this object if it's not an alias itself.
+ */
+ List aliases = new ArrayList() ;
+
+ /**
+ * The base initialize() implementation. This takes a ConfigCommand with
+ * three arguments: the command name, the instance name, and the name of
+ * the target class this ConfigObject is configuring. The command in the
+ * configuration file should have the form:<p>
+ *
+ * (New{configType} {instanceName} {className})<p>
+ *
+ * For example, (NewDevice tracker com.sun.j3d.input.LogitechTracker) will
+ * first cause ConfigDevice to be instantiated, which will then be
+ * initialized with this method. After all the properties are collected,
+ * ConfigDevice will instantiate com.sun.j3d.input.LogitechTracker,
+ * evaluate its properties, and allow references to it in the
+ * configuration file by the name "tracker".<p>
+ *
+ * It's assumed the target class will be instantiated through
+ * introspection and its properties set through introspection as well.
+ * Most config objects (ConfigScreen, ConfigView, ConfigViewPlatform,
+ * ConfigPhysicalBody, and ConfigPhysicalEnvironment) target a concrete
+ * core Java 3D class and will instantiate them directly, so they override
+ * this method.
+ *
+ * @param c the command that created this ConfigObject
+ */
+ protected void initialize(ConfigCommand c) {
+ if (c.argc != 3) {
+ syntaxError("Wrong number of arguments to " + c.commandName) ;
+ }
+
+ if (!isName(c.argv[1])) {
+ syntaxError("The first argument to " + c.commandName +
+ " must be the instance name") ;
+ }
+
+ if (!isName(c.argv[2])) {
+ syntaxError("The second argument to " + c.commandName +
+ " must be the class name") ;
+ }
+
+ targetClassName = (String)c.argv[2] ;
+ }
+
+ /**
+ * The base setProperty() implementation. This implementation assumes the
+ * property needs to be set by introspection on the property name as a
+ * method that accepts an array of Objects. That is, the command in the
+ * configuration file is of the form:<p>
+ *
+ * ({type}Property {instance name} {method name} {arg0} ... {argn})<p>
+ *
+ * For example, (DeviceProperty tracker SerialPort "/dev/ttya") will
+ * invoke the method named "SerialPort" in the object referenced by
+ * "tracker" with an array of 1 Object containing the String
+ * "/dev/ttya".<p>
+ *
+ * The property is stored as the original ConfigCommand and is evaluated
+ * after the configuration file has been parsed. It is overridden by
+ * subclasses that instantiate concrete core Java 3D classes with known
+ * method names.
+ *
+ * @param c the command that invoked this method
+ */
+ protected void setProperty(ConfigCommand c) {
+ if (c.argc < 4) {
+ syntaxError("Wrong number of arguments to " + c.commandName) ;
+ }
+
+ if (!isName(c.argv[1])) {
+ syntaxError("The first argument to " + c.commandName +
+ " must be the instance name") ;
+ }
+
+ if (!isName(c.argv[2])) {
+ syntaxError("The second argument to " + c.commandName +
+ " must be the property name") ;
+ }
+
+ properties.add(c) ;
+ }
+
+ /**
+ * Instantiates the target object.
+ */
+ protected Object createTargetObject() {
+ if (targetClassName == null)
+ return null ;
+
+ targetClass = getClassForName(creatingCommand, targetClassName) ;
+ targetObject = getNewInstance(creatingCommand, targetClass) ;
+
+ return targetObject ;
+ }
+
+ /**
+ * Return the class for the specified class name string.
+ *
+ * @param className the name of the class
+ * @return the object representing the class
+ */
+ protected Class getClassForName(ConfigCommand cmd, String className) {
+ try {
+ // Use the system class loader. If the Java 3D jar files are
+ // installed directly in the JVM's lib/ext directory, then the
+ // default class loader won't find user classes outside ext.
+ return Class.forName(className, true,
+ ClassLoader.getSystemClassLoader()) ;
+ }
+ catch (ClassNotFoundException e) {
+ throw new IllegalArgumentException
+ (errorMessage(cmd, "Class \"" + className + "\" not found")) ;
+ }
+ }
+
+ /**
+ * Return an instance of the class specified by the given class object.
+ *
+ * @param objectClass the object representing the class
+ * @return a new instance of the class
+ */
+ protected Object getNewInstance(ConfigCommand cmd, Class objectClass) {
+ try {
+ return objectClass.newInstance() ;
+ }
+ catch (IllegalAccessException e) {
+ throw new IllegalArgumentException
+ (errorMessage(cmd, "Illegal access to object class")) ;
+ }
+ catch (InstantiationException e) {
+ throw new IllegalArgumentException
+ (errorMessage(cmd, "Instantiation error for object class")) ;
+ }
+ }
+
+ /**
+ * Evaluate properties for the the given class instance. The property
+ * names are used as the names of methods to be invoked by the instance.
+ * Each such method takes an array of Objects as its only parameter. The
+ * array will contain Objects corresponding to the property values.
+ */
+ protected void processProperties() {
+ evaluateProperties(this.targetClass,
+ this.targetObject, this.properties) ;
+
+ // Potentially holds a lot of references, and not needed anymore.
+ this.properties.clear() ;
+ }
+
+ /**
+ * Evaluate properties for the the given class instance.
+ *
+ * @param objectClass the class object representing the given class
+ * @param objectInstance the class instance whose methods will be invoked
+ * @param properties list of property setting commands
+ */
+ protected void evaluateProperties(Class objectClass,
+ Object objectInstance,
+ List properties) {
+
+ // Holds the single parameter passed to the class instance methods.
+ Object[] parameters = new Object[1] ;
+
+ // Specifies the class of the single method parameter.
+ Class[] parameterTypes = new Class[1] ;
+
+ // All property methods use Object[] as their single parameter, which
+ // happens to be the same type as the parameters variable above.
+ parameterTypes[0] = parameters.getClass() ;
+
+ // Loop through all property commands and invoke the appropriate
+ // method for each one. Property commands are of the form:
+ // ({configClass}Property {instanceName} {methodName} {arg} ...)
+ for (int i = 0 ; i < properties.size() ; i++) {
+ ConfigCommand cmd = (ConfigCommand)properties.get(i) ;
+ String methodName = (String)cmd.argv[2] ;
+ Object[] argv = new Object[cmd.argc - 3] ;
+
+ for (int a = 0 ; a < argv.length ; a++) {
+ argv[a] = cmd.argv[a + 3] ;
+ if (argv[a] instanceof ConfigCommand) {
+ // Evaluate a delayed built-in command.
+ ConfigCommand bcmd = (ConfigCommand)argv[a] ;
+ argv[a] = configContainer.evaluateBuiltIn(bcmd) ;
+ }
+ }
+
+ parameters[0] = argv ;
+
+ try {
+ Method objectMethod =
+ objectClass.getMethod(methodName, parameterTypes) ;
+
+ objectMethod.invoke(objectInstance, parameters) ;
+ }
+ catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException
+ (errorMessage
+ (cmd, "Unknown property \"" + methodName + "\"")) ;
+ }
+ catch (IllegalAccessException e) {
+ throw new IllegalArgumentException
+ (errorMessage
+ (cmd, "Illegal access to \"" + methodName + "\"")) ;
+ }
+ catch (InvocationTargetException e) {
+ throw new IllegalArgumentException
+ (errorMessage(cmd, e.getTargetException().getMessage())) ;
+ }
+ }
+ }
+
+ /**
+ * Throws an IllegalArgumentException with the specified description.
+ * This is caught by the parser which prints out error diagnostics and
+ * continues parsing if it can.
+ *
+ * @param s string describing the syntax error
+ */
+ protected void syntaxError(String s) {
+ throw new IllegalArgumentException(s) ;
+ }
+
+ /**
+ * Constructs an error message from the given string and file information
+ * from the given command.
+ */
+ static String errorMessage(ConfigCommand cmd, String s) {
+ return
+ "\n" + s + "\nat line " + cmd.lineNumber +
+ " in " + cmd.fileName + "\n" + cmd + "\n" ;
+ }
+
+ /**
+ * Check if the argument is a name string.
+ *
+ * @param o the object to be checked
+ * @return true if the object is an instance of String
+ */
+ protected boolean isName(Object o) {
+ return (o instanceof String) ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/universe/ConfigPhysicalBody.java b/src/classes/share/com/sun/j3d/utils/universe/ConfigPhysicalBody.java
new file mode 100644
index 0000000..f735060
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/ConfigPhysicalBody.java
@@ -0,0 +1,178 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.universe;
+
+import java.awt.event.*;
+import java.lang.Integer;
+import java.io.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import java.util.*;
+
+class ConfigPhysicalBody extends ConfigObject {
+
+ Point3d leftEyePosition = new Point3d(-0.033, 0.0, 0.0);
+ Point3d rightEyePosition = new Point3d(0.033, 0.0, 0.0);
+ double stereoEyeSeparation = Double.MAX_VALUE;
+
+ Point3d leftEarPosition = new Point3d(-0.080, -0.030, 0.09);
+ Point3d rightEarPosition = new Point3d(0.080, -0.030, 0.09);
+
+ double nominalEyeHeightFromGround = 1.68;
+ double nominalEyeOffsetFromNominalScreen = 0.4572;
+
+ Matrix4d headToHeadTracker = new Matrix4d(
+ 1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0);
+
+ PhysicalBody j3dPhysicalBody;
+
+ // Overridden to do nothing.
+ protected void initialize(ConfigCommand command) {
+ }
+
+ protected void setProperty(ConfigCommand command) {
+ int argc = command.argc;
+ Object[] argv = command.argv;
+ String prop;
+ Object val;
+
+ // Check that arg[1] and arg[2] are strings
+ if (argc != 4) {
+ syntaxError("Incorrect number of arguments to " +
+ command.commandName);
+ }
+
+ if (!isName(argv[1])) {
+ syntaxError("The first argument to " + command.commandName +
+ " must be a name");
+ }
+
+ if (!isName(argv[2])) {
+ syntaxError("The second argument to " + command.commandName +
+ " must be an property/attribute name");
+ }
+
+ prop = (String) argv[2];
+ val = argv[3];
+
+ if (prop.equals("StereoEyeSeparation")) {
+ if (!(val instanceof Double)) {
+ syntaxError("StereoEyeSeparation must be a number");
+ }
+ stereoEyeSeparation = ((Double) val).doubleValue();
+ }
+ else if (prop.equals("LeftEyePosition")) {
+ if (!(val instanceof Point3d)) {
+ syntaxError("LeftEyePosition must be a point");
+ }
+ leftEyePosition = (Point3d) val;
+ }
+ else if (prop.equals("RightEyePosition")) {
+ if (!(val instanceof Point3d)) {
+ syntaxError("RightEyePosition must be a point");
+ }
+ rightEyePosition = (Point3d) val;
+ }
+ else if (prop.equals("LeftEarPosition")) {
+ if (!(val instanceof Point3d)) {
+ syntaxError("LeftEarPosition must be a point");
+ }
+ leftEarPosition = (Point3d) val;
+ }
+ else if (prop.equals("RightEarPosition")) {
+ if (!(val instanceof Point3d)) {
+ syntaxError("RightEarPosition must be a point");
+ }
+ leftEarPosition = (Point3d) val;
+ }
+ else if (prop.equals("NominalEyeHeightFromGround")) {
+ if (!(val instanceof Double)) {
+ syntaxError("NominalEyeHeightFromGround must be a number");
+ }
+ nominalEyeHeightFromGround = ((Double) val).doubleValue();
+ }
+ else if (prop.equals("NominalEyeOffsetFromNominalScreen")) {
+ if (!(val instanceof Double)) {
+ syntaxError("NominalEyeOffsetFromNominalScreen " +
+ "must be a number");
+ }
+ nominalEyeOffsetFromNominalScreen = ((Double) val).doubleValue();
+ }
+ else if (prop.equals("HeadToHeadTracker")) {
+ if (!(val instanceof Matrix4d)) {
+ syntaxError("HeadToHeadTracker must be a matrix");
+ }
+ headToHeadTracker = (Matrix4d) val;
+ }
+ else {
+ syntaxError("Unknown " + command.commandName +
+ " \"" + prop + "\"") ;
+ }
+ }
+
+ PhysicalBody createJ3dPhysicalBody() {
+ // Transfer all the information from the config version
+ if (stereoEyeSeparation < Double.MAX_VALUE) {
+ leftEyePosition.set(-stereoEyeSeparation / 2.0, 0.0, 0.0);
+ rightEyePosition.set(stereoEyeSeparation / 2.0, 0.0, 0.0);
+ }
+
+ j3dPhysicalBody = new PhysicalBody(leftEyePosition, rightEyePosition,
+ leftEarPosition, rightEarPosition);
+
+ j3dPhysicalBody.setHeadToHeadTracker(
+ new Transform3D(headToHeadTracker));
+
+ j3dPhysicalBody.setNominalEyeHeightFromGround(
+ nominalEyeHeightFromGround);
+ j3dPhysicalBody.setNominalEyeOffsetFromNominalScreen(
+ nominalEyeOffsetFromNominalScreen);
+
+ return j3dPhysicalBody ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/universe/ConfigPhysicalEnvironment.java b/src/classes/share/com/sun/j3d/utils/universe/ConfigPhysicalEnvironment.java
new file mode 100644
index 0000000..d228773
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/ConfigPhysicalEnvironment.java
@@ -0,0 +1,179 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.universe ;
+
+import javax.media.j3d.* ;
+import javax.vecmath.* ;
+import java.util.* ;
+
+class ConfigPhysicalEnvironment extends ConfigObject {
+
+ /**
+ * The corresponding J3D core PhysicalEnvironment instance.
+ */
+ PhysicalEnvironment j3dPhysicalEnvironment = null ;
+
+ /**
+ * The coexistence to tracker base matrix.
+ */
+ Matrix4d coexistenceToTrackerBase = null ;
+
+ // All other configurable attributes.
+ private ConfigSensor headTracker = null ;
+ private ArrayList inputDevices = new ArrayList() ;
+ private int coexistenceCenterInPworldPolicy = View.NOMINAL_SCREEN ;
+
+ /**
+ * Overrides initialize() to do nothing.
+ */
+ protected void initialize(ConfigCommand command) {
+ }
+
+ /**
+ * Handles the commands
+ * (PhysicalEnvironmentAttribute {instance} {attrName} {attrValue}) and
+ * (PhysicalEnvironmentProperty {instance} {attrName} {attrValue}).
+ *
+ * @param command the command that invoked this method
+ */
+ protected void setProperty(ConfigCommand command) {
+ Object val ;
+ Object[] argv = command.argv ;
+ int argc = command.argc ;
+ String sval, prop ;
+
+ if (argc != 4) {
+ syntaxError("Incorrect number of arguments to " +
+ command.commandName) ;
+ }
+
+ if (!isName(argv[1])) {
+ syntaxError("The first argument to " + command.commandName +
+ " must be a name") ;
+ }
+
+ if (!isName(argv[2])) {
+ syntaxError("The second argument to " + command.commandName +
+ " must be a property name") ;
+ }
+
+ prop = (String)argv[2] ;
+ val = argv[3] ;
+
+ if (prop.equals("CoexistenceCenterInPworldPolicy")) {
+ if (!(val instanceof String))
+ syntaxError("CoexistenceCenterInPworldPolicy must be string") ;
+
+ sval = (String)val ;
+ if (sval.equals("NOMINAL_HEAD"))
+ coexistenceCenterInPworldPolicy = View.NOMINAL_HEAD ;
+ else if (sval.equals("NOMINAL_SCREEN"))
+ coexistenceCenterInPworldPolicy = View.NOMINAL_SCREEN ;
+ else if (sval.equals("NOMINAL_FEET"))
+ coexistenceCenterInPworldPolicy = View.NOMINAL_FEET ;
+ else
+ syntaxError("Illegal value " + sval +
+ " for CoexistenceCenterInPworldPolicy") ;
+ }
+ else if (prop.equals("CoexistenceToTrackerBase")) {
+ if (val instanceof Matrix4d)
+ coexistenceToTrackerBase = (Matrix4d)val ;
+ else
+ syntaxError("CoexistenceToTrackerBase must be a Matrix4d") ;
+ }
+ else if (prop.equals("InputDevice")) {
+ if (!(val instanceof String))
+ syntaxError("InputDevice must be a name") ;
+
+ sval = (String)val ;
+ inputDevices.add(configContainer.findConfigObject("Device", sval));
+ }
+ else if (prop.equals("HeadTracker")) {
+ if (!(val instanceof String))
+ syntaxError("HeadTracker must be a Sensor name") ;
+
+ sval = (String)val ;
+ headTracker =
+ (ConfigSensor)configContainer.findConfigObject("Sensor", sval);
+ }
+ else {
+ syntaxError("Unknown " + command.commandName +
+ " \"" + prop + "\"") ;
+ }
+ }
+
+ /**
+ * Create a core Java 3D PhysicalEnvironment instance using the attributes
+ * gathered by this object.
+ */
+ PhysicalEnvironment createJ3dPhysicalEnvironment() {
+ j3dPhysicalEnvironment = new PhysicalEnvironment() ;
+
+ j3dPhysicalEnvironment.setCoexistenceCenterInPworldPolicy
+ (coexistenceCenterInPworldPolicy) ;
+
+ if (coexistenceToTrackerBase != null)
+ j3dPhysicalEnvironment.setCoexistenceToTrackerBase
+ (new Transform3D(coexistenceToTrackerBase)) ;
+
+ return j3dPhysicalEnvironment ;
+ }
+
+ /**
+ * Process the devices associated with the PhysicalEnvironment.
+ */
+ void processDevices() {
+ for (int j = 0; j < inputDevices.size(); j++) {
+ ConfigDevice configDevice = (ConfigDevice)inputDevices.get(j) ;
+ InputDevice device = configDevice.j3dInputDevice ;
+ j3dPhysicalEnvironment.addInputDevice(device) ;
+ }
+
+ if (headTracker != null) {
+ j3dPhysicalEnvironment.setHeadIndex(0) ;
+ j3dPhysicalEnvironment.setSensor(0, headTracker.j3dSensor) ;
+ }
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/universe/ConfigScreen.java b/src/classes/share/com/sun/j3d/utils/universe/ConfigScreen.java
new file mode 100644
index 0000000..f17a0ce
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/ConfigScreen.java
@@ -0,0 +1,307 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.universe ;
+
+import java.awt.Window ;
+import javax.media.j3d.Canvas3D ;
+import javax.media.j3d.View ;
+import javax.swing.JFrame ;
+import javax.swing.JPanel ;
+import javax.vecmath.Matrix4d ;
+import javax.vecmath.Point2d ;
+
+class ConfigScreen extends ConfigObject {
+
+ /**
+ * The index of this screen in the GraphicsDevice array returned by
+ * GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices().
+ */
+ int frameBufferNumber ;
+
+ /**
+ * The physical width in meters of the screen area of the GraphicsDevice
+ * associated with this ConfigScreen. The default is based on a screen
+ * resolution of 90 pixels/inch.
+ */
+ double physicalScreenWidth = 0.0 ;
+
+ /**
+ * The physical height in meters of the screen area of the GraphicsDevice
+ * associated with this ConfigScreen. The default is based on a screen
+ * resolution of 90 pixels/inch.
+ */
+ double physicalScreenHeight = 0.0 ;
+
+ /**
+ * The trackerBaseToImagePlate transform of this ConfigScreen.
+ * The default is the identity transform.
+ */
+ Matrix4d trackerBaseToImagePlate = null ;
+
+ /**
+ * The headTrackerToLeftImagePlate transform of this ConfigScreen if HMD
+ * mode is in effect. The default is the identity transform.
+ */
+ Matrix4d headTrackerToLeftImagePlate = null ;
+
+ /**
+ * The headTrackerToRightImagePlate transform of this ConfigScreen if HMD
+ * mode is in effect. The default is the identity transform.
+ */
+ Matrix4d headTrackerToRightImagePlate = null ;
+
+ /**
+ * The monoscopicViewPolicy for this ConfigScreen. The default is
+ * View.CYCLOPEAN_EYE_VIEW.
+ */
+ int monoscopicViewPolicy = View.CYCLOPEAN_EYE_VIEW ;
+
+ /**
+ * Boolean indicating whether a full-screen window should be created for
+ * this ConfigScreen. The default is false.
+ */
+ boolean fullScreen = false ;
+
+ /**
+ * Boolean indicating whether a full-screen window with no borders should
+ * be created for this ConfigScreen. The default is false.
+ */
+ boolean noBorderFullScreen = false ;
+
+ /**
+ * The width in pixels for the window to be created for this ConfigScreen
+ * if a full screen window is not specified. The default is 512.
+ */
+ int windowWidthInPixels = 512 ;
+
+ /**
+ * The height in pixels for the window to be created for this ConfigScreen
+ * if a full screen window is not specified. The default is 512.
+ */
+ int windowHeightInPixels = 512 ;
+
+ /**
+ * The X pixel position of the top-left corner of the window, relative to
+ * the physical screen. The default is 0.
+ */
+ int windowX = 0 ;
+
+ /**
+ * The Y pixel position of the top-left corner of the window, relative to
+ * the physical screen. The default is 0.
+ */
+ int windowY = 0 ;
+
+ /**
+ * The JFrame created for this ConfigScreen. When running under JDK 1.4
+ * or newer, the JFrame always contains a JPanel which contains the
+ * Canvas3D. When running under JDK 1.3.1 and using a borderless full
+ * screen the JFrame will instead contain a JWindow which will contain the
+ * JPanel and Canvas3D.
+ */
+ JFrame j3dJFrame ;
+
+ /**
+ * The Window created for this ConfigScreen. Under JDK 1.4 or higher this
+ * is the same reference as j3dJFrame. If a borderless full screen is
+ * specified while running under JDK 1.3.1 then this is a JWindow with the
+ * j3dJFrame as its parent.
+ */
+ Window j3dWindow ;
+
+ /**
+ * The JPanel created for this ConfigScreen to hold the Canvas3D.
+ */
+ JPanel j3dJPanel ;
+
+ /**
+ * The Canvas3D created for this ConfigScreen.
+ */
+ Canvas3D j3dCanvas ;
+
+ /**
+ * Processes attributes for this object. Handles commands of the form:<p>
+ * (ScreenAttribute {instanceName} {attrName} {attrValue})
+ * (ScreenProperty {instanceName} {attrName} {attrValue})
+ * (DisplayAttribute {instanceName} {attrName} {attrValue})
+ * (DisplayProperty {instanceName} {attrName} {attrValue})
+ *
+ * @param command the command that invoked this method
+ */
+ protected void setProperty(ConfigCommand command) {
+
+ String attr = null ;
+ Object val = null ;
+ String sval = null ;
+
+ if (command.argc != 4) {
+ syntaxError("Incorrect number of arguments to " +
+ command.commandName) ;
+ }
+
+ if (!isName(command.argv[2])) {
+ syntaxError("The second argument to " + command.commandName +
+ " must be a property name") ;
+ }
+
+ attr = (String)command.argv[2] ;
+ val = command.argv[3] ;
+
+ if (attr.equals("PhysicalScreenWidth")) {
+ if (!(val instanceof Double)) {
+ syntaxError("Value for PhysicalScreenWidth " +
+ "must be a number") ;
+ }
+ physicalScreenWidth = ((Double)val).doubleValue() ;
+ }
+ else if (attr.equals("PhysicalScreenHeight")) {
+ if (!(val instanceof Double)) {
+ syntaxError("Value for PhysicalScreenHeight " +
+ "must be a number") ;
+ }
+ physicalScreenHeight = ((Double)val).doubleValue() ;
+ }
+ else if (attr.equals("TrackerBaseToImagePlate")) {
+ if (!(val instanceof Matrix4d)) {
+ syntaxError("Value for TrackerBaseToImagePlate " +
+ "must be a 4x3 or 4x4 matrix") ;
+ }
+ trackerBaseToImagePlate = (Matrix4d)val ;
+ }
+ else if (attr.equals("HeadTrackerToLeftImagePlate")) {
+ if (!(val instanceof Matrix4d)) {
+ syntaxError("Value for HeadTrackerToLeftImagePlate "
+ + "must be a 4x3 or 4x4 matrix") ;
+ }
+ headTrackerToLeftImagePlate = (Matrix4d)val ;
+ }
+ else if (attr.equals("HeadTrackerToRightImagePlate")) {
+ if (!(val instanceof Matrix4d)) {
+ syntaxError("Value for HeadTrackerToRightImagePlate "
+ + "must be a 4x3 or 4x4 matrix") ;
+ }
+ headTrackerToRightImagePlate = (Matrix4d)val ;
+ }
+ else if (attr.equals("MonoscopicViewPolicy")) {
+ if (!(val instanceof String)) {
+ syntaxError("Value for MonoscopicViewPolicy " +
+ "must be a name") ;
+ }
+ sval = (String)val ;
+ if (sval.equals("LEFT_EYE_VIEW"))
+ monoscopicViewPolicy = View.LEFT_EYE_VIEW ;
+ else if (sval.equals("RIGHT_EYE_VIEW"))
+ monoscopicViewPolicy = View.RIGHT_EYE_VIEW ;
+ else if (sval.equals("CYCLOPEAN_EYE_VIEW"))
+ monoscopicViewPolicy = View.CYCLOPEAN_EYE_VIEW ;
+ else
+ syntaxError("Invalid value for MonoscopicViewPolicy "
+ + "\"" + sval + "\"") ;
+ }
+ else if (attr.equals("WindowPosition")) {
+ if (! (val instanceof Point2d)) {
+ syntaxError("WindowPosition must be a Point2d") ;
+ }
+ Point2d p2d = (Point2d)val ;
+ windowX = (int)p2d.x ;
+ windowY = (int)p2d.y ;
+ }
+ else if (attr.equals("WindowSize")) {
+ if (val instanceof Point2d) {
+ fullScreen = false ;
+ noBorderFullScreen = false ;
+
+ Point2d p2d = (Point2d)val ;
+ windowWidthInPixels = (int)p2d.x ;
+ windowHeightInPixels = (int)p2d.y ;
+ }
+ else if (val instanceof String) {
+ String s = (String)val ;
+
+ if (s.equals("FullScreen")) {
+ fullScreen = true ;
+ noBorderFullScreen = false ;
+ } else if (s.equals("NoBorderFullScreen")) {
+ fullScreen = false ;
+ noBorderFullScreen = true ;
+ } else {
+ syntaxError("Value for WindowSize " +
+ "must be one of\n" + "\"FullScreen\" " +
+ "\"NoBorderFullScreen\" or Point2d") ;
+ }
+ }
+ else {
+ syntaxError("Invalid WindowSize value: " + val +
+ "\nValue for WindowSize " +
+ "must be one of\n" + "\"FullScreen\" " +
+ "\"NoBorderFullScreen\" or Point2d") ;
+ }
+ }
+ else {
+ syntaxError("Unknown " + command.commandName +
+ " \"" + attr + "\"") ;
+ }
+ }
+
+ /**
+ * Initializes this object. Handles commands of the form:<p>
+ * (NewScreen {instanceName} {FrameBufferNumber}).
+ *
+ * @param command the command that invoked this method
+ */
+ protected void initialize(ConfigCommand command) {
+ if (command.argc != 3) {
+ syntaxError("Incorrect number of arguments to " +
+ command.commandName) ;
+ }
+
+ if (!(command.argv[2] instanceof Double)) {
+ syntaxError("The second argument to " + command.commandName +
+ " must be a GraphicsDevice index") ;
+ }
+
+ frameBufferNumber = ((Double)command.argv[2]).intValue() ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/universe/ConfigSensor.java b/src/classes/share/com/sun/j3d/utils/universe/ConfigSensor.java
new file mode 100644
index 0000000..38aa779
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/ConfigSensor.java
@@ -0,0 +1,211 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.universe ;
+
+import javax.vecmath.Point3d ;
+import javax.media.j3d.Sensor ;
+
+class ConfigSensor extends ConfigObject {
+
+ // The index of this sensor in the associated InputDevice.
+ private int sensorIndex ;
+
+ // The ConfigDevice which creates the associated InputDevice.
+ private ConfigDevice configDevice ;
+
+ // All configurable attributes.
+ private Point3d hotspot = null ;
+ private int predictor = -1 ;
+ private int predictionPolicy = -1 ;
+ private int sensorReadCount = -1 ;
+
+ /**
+ * The corresponding Java 3D core Sensor instance. This is created by the
+ * associated InputDevice.
+ */
+ Sensor j3dSensor ;
+
+ /**
+ * Handles the command
+ * (NewSensor {instanceName} {inputDeviceName} {indexInInputDevice})
+ *
+ * @param command the command that invoked this method
+ */
+ protected void initialize(ConfigCommand command) {
+
+ int argc = command.argc ;
+ Object[] argv = command.argv ;
+
+ // Check that arg[1] and arg[2] are strings, arg[3] a number
+ if (argc != 4) {
+ syntaxError("Incorrect number of arguments to " +
+ command.commandName) ;
+ }
+
+ if (!isName(argv[2])) {
+ syntaxError("The second argument to " + command.commandName +
+ " must be the device name") ;
+ }
+
+ if (!(argv[3] instanceof Double)) {
+ syntaxError("The third argument to " + command.commandName +
+ " must be a sensor index") ;
+ }
+
+ sensorIndex = ((Double)argv[3]).intValue() ;
+ configDevice = (ConfigDevice)configContainer.findConfigObject
+ ("Device", (String)argv[2]) ;
+ }
+
+ /**
+ * Handles the commands
+ * (SensorAttribute {instanceName} {attributeName} {attributeValue}) and
+ * (SensorProperty {instanceName} {attributeName} {attributeValue}).
+ *
+ * @param command the command that invoked this method
+ */
+ protected void setProperty(ConfigCommand command) {
+
+ int argc = command.argc ;
+ Object[] argv = command.argv ;
+ String attribute ;
+
+ // Check that arg[1] and arg[2] are strings
+ if (argc != 4) {
+ syntaxError("Incorrect number of arguments to " +
+ command.commandName) ;
+ }
+
+ if (! isName(argv[1])) {
+ syntaxError("The first argument to " + command.commandName +
+ " must be the instance name") ;
+ }
+
+ if (! isName(argv[2])) {
+ syntaxError("The second argument to " + command.commandName +
+ " must be a property name") ;
+ }
+
+ attribute = (String)argv[2] ;
+ if (attribute.equals("Hotspot")) {
+ if (! (argv[3] instanceof Point3d)) {
+ syntaxError("Hotspot must be a 3D point") ;
+ }
+ hotspot = (Point3d)argv[3] ;
+ }
+ /*
+ * Questionable attributes. Commented out for now.
+ else if (attribute.equals("Predictor")) {
+ if (! isName(argv[3])) {
+ syntaxError("Predictor must be a name") ;
+ }
+
+ String predictorName = (String)argv[3] ;
+ if (predictorName.equals("PREDICT_NONE")) {
+ predictor = Sensor.PREDICT_NONE ;
+ }
+ else if (predictorName.equals("PREDICT_NEXT_FRAME_TIME")) {
+ predictor = Sensor.PREDICT_NEXT_FRAME_TIME ;
+ }
+ else {
+ syntaxError("Predictor must be either PREDICT_NONE " +
+ "or PREDICT_NEXT_FRAME_TIME") ;
+ }
+ }
+ else if (attribute.equals("PredictionPolicy")) {
+ if (! isName(argv[3])) {
+ syntaxError("PredictionPolicy must be a name") ;
+ }
+
+ String predictionPolicyName = (String)argv[3] ;
+ if (predictionPolicyName.equals("NO_PREDICTOR")) {
+ predictionPolicy = Sensor.NO_PREDICTOR ;
+ }
+ else if (predictionPolicyName.equals("HEAD_PREDICTOR")) {
+ predictionPolicy = Sensor.HEAD_PREDICTOR ;
+ }
+ else if (predictionPolicyName.equals("HAND_PREDICTOR")) {
+ predictionPolicy = Sensor.HAND_PREDICTOR ;
+ }
+ else {
+ syntaxError("PredictionPolicy must be either NO_PREDICTOR, " +
+ "HEAD_PREDICTOR, or HAND_PREDICTOR") ;
+ }
+ }
+ else if (attribute.equals("SensorReadCount")) {
+ if (! (argv[3] instanceof Double)) {
+ syntaxError("SensorReadCount must be a number") ;
+ }
+ sensorReadCount = ((Double)argv[3]).intValue() ;
+ }
+ */
+ else {
+ syntaxError("Unknown " + command.commandName +
+ " \"" + attribute + "\"") ;
+ }
+ }
+
+ /**
+ * This method is called after all InputDevice implementations have been
+ * instantiated and initialized. All the specified attributes for this
+ * sensor are set in the corresponding Java3D core Sensor instantiated by
+ * the associated InputDevice.
+ */
+ void configureSensor() {
+ j3dSensor = configDevice.j3dInputDevice.getSensor(sensorIndex) ;
+
+ if (hotspot != null)
+ j3dSensor.setHotspot(hotspot) ;
+
+ if (predictor != -1)
+ j3dSensor.setPredictor(predictor) ;
+
+ if (predictionPolicy != -1)
+ j3dSensor.setPredictionPolicy(predictionPolicy) ;
+
+ if (sensorReadCount != -1)
+ j3dSensor.setSensorReadCount(sensorReadCount) ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/universe/ConfigSexpression.java b/src/classes/share/com/sun/j3d/utils/universe/ConfigSexpression.java
new file mode 100644
index 0000000..075e63c
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/ConfigSexpression.java
@@ -0,0 +1,569 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.universe ;
+
+import java.awt.event.* ;
+import java.io.* ;
+import java.lang.Integer ;
+import java.lang.Boolean ;
+import java.util.* ;
+import javax.vecmath.* ;
+import javax.media.j3d.* ;
+
+class ConfigSexpression {
+
+ private ArrayList elements = new ArrayList() ;
+
+ private void syntaxError(StreamTokenizer st, String file, String s) {
+ System.out.println(s + ":\nat line " + st.lineno() + " in " + file) ;
+ print() ; System.out.print("\n\n") ;
+ }
+
+ private int myNextToken(StreamTokenizer st, String file) {
+ int tok = 0 ;
+ try {
+ tok = st.nextToken() ;
+ }
+ catch (IOException e) {
+ throw new RuntimeException("\n" + e + "\nwhile reading " + file) ;
+ }
+ return tok ;
+ }
+
+
+ Object parseAndEval(ConfigContainer configContainer,
+ StreamTokenizer st, int level) {
+ int tok ;
+ String s ;
+ String file = configContainer.currentFileName ;
+
+ //
+ // First tokenize the character stream and add the tokens to the
+ // elements array.
+ //
+ elements.clear() ;
+
+ // Look for an open paren to start this sexp.
+ while (true) {
+ tok = myNextToken(st, file) ;
+
+ if (tok == StreamTokenizer.TT_EOF)
+ return Boolean.FALSE ;
+
+ if (tok == ')')
+ syntaxError(st, file, "Premature closing parenthesis") ;
+
+ if (tok == '(')
+ break ;
+ }
+
+ // Add elements until a close paren for this sexp is found.
+ for (int i = 0 ; true ; i++) {
+ tok = myNextToken(st, file) ;
+
+ if (tok == StreamTokenizer.TT_EOF) {
+ syntaxError(st, file, "Missing closing parenthesis") ;
+ break ;
+ }
+
+ // An open paren starts a new embedded sexp. Put the paren back,
+ // evaluate the sexp, and add the result to the elements list.
+ if (tok == '(') {
+ st.pushBack() ;
+ ConfigSexpression cs = new ConfigSexpression() ;
+ elements.add(cs.parseAndEval(configContainer, st, level+1)) ;
+ continue ;
+ }
+
+ // A close paren finishes the scan.
+ if (tok == ')')
+ break ;
+
+ // Check for too many arguments.
+ if (i >= 20)
+ syntaxError(st, file, "Too many arguments") ;
+
+ // Check for numeric argument.
+ if (tok == StreamTokenizer.TT_NUMBER) {
+ elements.add(new Double(st.nval)) ;
+ continue ;
+ }
+
+ // Anything other than a word or a quoted string is an error.
+ if (tok != StreamTokenizer.TT_WORD && tok != '"' && tok != '\'') {
+ String badToken = String.valueOf((char)tok) ;
+ elements.add(badToken) ; // so bad token prints out
+ syntaxError(st, file, "Invalid token \"" + badToken +
+ "\" must be enclosed in quotes") ;
+ continue ;
+ }
+
+ // Scan the token for Java property substitution syntax ${...}.
+ s = scanJavaProperties(st, file, st.sval) ;
+ if (s == null) continue ;
+
+ if (s.equalsIgnoreCase("true"))
+ // Replace "true" or "True" with the Boolean equivalent.
+ elements.add(new Boolean(true)) ;
+
+ else if (s.equalsIgnoreCase("false"))
+ // Replace "false" or "False" with the Boolean equivalent.
+ elements.add(new Boolean(false)) ;
+
+ else
+ // Add the token as a string element.
+ elements.add(s) ;
+ }
+
+
+ //
+ // Now evaluate elements.
+ //
+ if (elements.size() == 0)
+ syntaxError(st, file, "Null command") ;
+
+ // If the first argument is a string, then this sexp must be
+ // a top-level command or a built-in, and needs to be evaluated.
+ if (elements.get(0) instanceof String) {
+ try {
+ if (level == 0) {
+ configContainer.evaluateCommand(elements, st.lineno()) ;
+
+ // Continue parsing top-level commands.
+ return Boolean.TRUE ;
+ }
+ else {
+ // Evaluate built-in and return result to next level up.
+ return evaluateBuiltIn
+ (configContainer, elements, st.lineno()) ;
+ }
+ }
+ catch (IllegalArgumentException e) {
+ syntaxError(st, file, e.getMessage()) ;
+ if (level == 0)
+ // Command ignored: continue parsing.
+ return Boolean.TRUE ;
+ else
+ // Function ignored: return sexp to next level up so error
+ // processing can print it out in context of command.
+ return this ;
+ }
+ }
+
+ // If the first argument isn't a string, and we are at level 0,
+ // this is a syntax error.
+ if (level == 0)
+ syntaxError(st, file, "Malformed top-level command name") ;
+
+ // If the first argument is a number, then we must have
+ // either a 2D, 3D, or 4D numeric vector.
+ if (elements.get(0) instanceof Double) {
+ if (elements.size() == 1)
+ syntaxError(st, file, "Can't have single-element vector") ;
+
+ // Point2D
+ if (elements.size() == 2) {
+ if (!(elements.get(1) instanceof Double))
+ syntaxError(st, file, "Both elements must be numbers") ;
+
+ return new Point2d(((Double)elements.get(0)).doubleValue(),
+ ((Double)elements.get(1)).doubleValue()) ;
+ }
+
+ // Point3d
+ if (elements.size() == 3) {
+ if (!(elements.get(1) instanceof Double) ||
+ !(elements.get(2) instanceof Double))
+ syntaxError(st, file, "All elements must be numbers") ;
+
+ return new Point3d(((Double)elements.get(0)).doubleValue(),
+ ((Double)elements.get(1)).doubleValue(),
+ ((Double)elements.get(2)).doubleValue()) ;
+ }
+
+ // Point4D
+ if (elements.size() == 4) {
+ if (!(elements.get(1) instanceof Double) ||
+ !(elements.get(2) instanceof Double) ||
+ !(elements.get(3) instanceof Double))
+ syntaxError(st, file, "All elements must be numbers") ;
+
+ return new Point4d(((Double)elements.get(0)).doubleValue(),
+ ((Double)elements.get(1)).doubleValue(),
+ ((Double)elements.get(2)).doubleValue(),
+ ((Double)elements.get(3)).doubleValue()) ;
+ }
+
+ // Anything else is an error.
+ syntaxError(st, file, "Too many vector elements") ;
+ }
+
+ // If the first argument is a Point3d, then we should be a Matrix3d.
+ if (elements.get(0) instanceof Point3d) {
+ if (elements.size() != 3)
+ syntaxError(st, file, "Matrix must have three rows") ;
+
+ if (!(elements.get(1) instanceof Point3d) ||
+ !(elements.get(2) instanceof Point3d))
+ syntaxError(st, file, "All rows must have three elements") ;
+
+ return new Matrix3d(((Point3d)elements.get(0)).x,
+ ((Point3d)elements.get(0)).y,
+ ((Point3d)elements.get(0)).z,
+ ((Point3d)elements.get(1)).x,
+ ((Point3d)elements.get(1)).y,
+ ((Point3d)elements.get(1)).z,
+ ((Point3d)elements.get(2)).x,
+ ((Point3d)elements.get(2)).y,
+ ((Point3d)elements.get(2)).z) ;
+ }
+
+ // If the first argument is a Point4d, then we should be a Matrix4d.
+ if (elements.get(0) instanceof Point4d) {
+ if (elements.size() == 3) {
+ if (!(elements.get(1) instanceof Point4d) ||
+ !(elements.get(2) instanceof Point4d))
+ syntaxError(st, file, "All rows must have four elements") ;
+
+ return new Matrix4d(((Point4d)elements.get(0)).x,
+ ((Point4d)elements.get(0)).y,
+ ((Point4d)elements.get(0)).z,
+ ((Point4d)elements.get(0)).w,
+ ((Point4d)elements.get(1)).x,
+ ((Point4d)elements.get(1)).y,
+ ((Point4d)elements.get(1)).z,
+ ((Point4d)elements.get(1)).w,
+ ((Point4d)elements.get(2)).x,
+ ((Point4d)elements.get(2)).y,
+ ((Point4d)elements.get(2)).z,
+ ((Point4d)elements.get(2)).w,
+ 0.0, 0.0, 0.0, 1.0) ;
+ }
+ else if (elements.size() != 4)
+ syntaxError(st, file, "Matrix must have three or four rows") ;
+
+ if (!(elements.get(1) instanceof Point4d) ||
+ !(elements.get(2) instanceof Point4d) ||
+ !(elements.get(3) instanceof Point4d))
+ syntaxError(st, file, "All rows must have four elements") ;
+
+ return new Matrix4d(((Point4d)elements.get(0)).x,
+ ((Point4d)elements.get(0)).y,
+ ((Point4d)elements.get(0)).z,
+ ((Point4d)elements.get(0)).w,
+ ((Point4d)elements.get(1)).x,
+ ((Point4d)elements.get(1)).y,
+ ((Point4d)elements.get(1)).z,
+ ((Point4d)elements.get(1)).w,
+ ((Point4d)elements.get(2)).x,
+ ((Point4d)elements.get(2)).y,
+ ((Point4d)elements.get(2)).z,
+ ((Point4d)elements.get(2)).w,
+ ((Point4d)elements.get(3)).x,
+ ((Point4d)elements.get(3)).y,
+ ((Point4d)elements.get(3)).z,
+ ((Point4d)elements.get(3)).w) ;
+ }
+
+ // Anything else is an error.
+ syntaxError(st, file, "Syntax error") ;
+ return null ;
+ }
+
+ /**
+ * Scan for Java properties in the specified string. Nested properties are
+ * not supported.
+ *
+ * @param st stream tokenizer in use
+ * @param f current file name
+ * @param s string containing non-nested Java properties possibly
+ * interspersed with arbitrary text.
+ * @return scanned string with Java properties replaced with values
+ */
+ private String scanJavaProperties(StreamTokenizer st, String f, String s) {
+ int open = s.indexOf("${") ;
+ if (open == -1) return s ;
+
+ int close = 0 ;
+ StringBuffer buf = new StringBuffer() ;
+ while (open != -1) {
+ buf.append(s.substring(close, open)) ;
+ close = s.indexOf('}', open) ;
+ if (close == -1) {
+ elements.add(s) ; // so that the bad element prints out
+ syntaxError(st, f, "Java property substitution syntax error") ;
+ return null ;
+ }
+
+ String property = s.substring(open + 2, close) ;
+ String value = ConfigCommand.evaluateJavaProperty(property) ;
+ if (value == null) {
+ elements.add(s) ; // so that the bad element prints out
+ syntaxError(st, f, "Java property \"" + property +
+ "\" has a null value") ;
+ return null ;
+ }
+
+ buf.append(value) ;
+ open = s.indexOf("${", close) ;
+ close++ ;
+ }
+
+ buf.append(s.substring(close)) ;
+ return buf.toString() ;
+ }
+
+ /**
+ * This method gets called from the s-expression parser to evaluate a
+ * built-in command.
+ *
+ * @param elements tokenized list of sexp elements
+ * @return object representing result of evaluation
+ */
+ private Object evaluateBuiltIn(ConfigContainer configContainer,
+ ArrayList elements, int lineNumber) {
+ int argc ;
+ String functionName ;
+
+ argc = elements.size() ;
+ functionName = (String)elements.get(0) ;
+
+ if (functionName.equals("Rotate")) {
+ return makeRotate(elements) ;
+ }
+ else if (functionName.equals("Translate")) {
+ return makeTranslate(elements) ;
+ }
+ else if (functionName.equals("RotateTranslate") ||
+ functionName.equals("TranslateRotate") ||
+ functionName.equals("Concatenate")) {
+
+ return concatenate(elements) ;
+ }
+ else if (functionName.equals("BoundingSphere")) {
+ return makeBoundingSphere(elements) ;
+ }
+ else {
+ // This built-in can't be evaluated immediately or contains an
+ // unknown command. Create a ConfigCommand for later evaluation.
+ return new ConfigCommand
+ (elements, configContainer.currentFileName, lineNumber) ;
+ }
+ }
+
+ /**
+ * Processes the built-in command (Translate x y z).
+ *
+ * @param elements ArrayList containing Doubles wrapping x, y, and z
+ * translation components at indices 1, 2, and 3 respectively
+ *
+ * @return matrix that translates by the given x, y, and z components
+ */
+ private Matrix4d makeTranslate(ArrayList elements) {
+ if (elements.size() != 4) {
+ throw new IllegalArgumentException
+ ("Incorrect number of arguments to Translate") ;
+ }
+
+ if (!(elements.get(1) instanceof Double) ||
+ !(elements.get(2) instanceof Double) ||
+ !(elements.get(3) instanceof Double)) {
+ throw new IllegalArgumentException
+ ("All arguments to Translate must be numbers") ;
+ }
+
+ Matrix4d m4d = new Matrix4d() ;
+ m4d.set(new Vector3d(((Double)elements.get(1)).doubleValue(),
+ ((Double)elements.get(2)).doubleValue(),
+ ((Double)elements.get(3)).doubleValue())) ;
+
+ return m4d ;
+ }
+
+ /**
+ * Processes the (Rotate x y z) built-in command.
+ *
+ * @param elements ArrayList containing Doubles wrapping x, y, and z Euler
+ * angles at indices 1, 2, and 3 respectively
+ *
+ * @return matrix that rotates by the given Euler angles around static X,
+ * Y, and Z basis vectors: first about X, then Y, and then Z
+ *
+ * @see Transform3D#setEuler()
+ */
+ private Matrix4d makeRotate(ArrayList elements) {
+ if (elements.size() != 4) {
+ throw new IllegalArgumentException
+ ("Incorrect number of arguments to Rotate") ;
+ }
+
+ if (!(elements.get(1) instanceof Double) ||
+ !(elements.get(2) instanceof Double) ||
+ !(elements.get(3) instanceof Double)) {
+ throw new IllegalArgumentException
+ ("All arguments to Rotate must be numbers") ;
+ }
+
+ double x = Math.toRadians(((Double)elements.get(1)).doubleValue()) ;
+ double y = Math.toRadians(((Double)elements.get(2)).doubleValue()) ;
+ double z = Math.toRadians(((Double)elements.get(3)).doubleValue()) ;
+
+ Transform3D t3d = new Transform3D() ;
+ t3d.setEuler(new Vector3d(x, y, z)) ;
+
+ Matrix4d m4d = new Matrix4d() ;
+ t3d.get(m4d) ;
+
+ return m4d ;
+ }
+
+ /**
+ * Processes the (RotateTranslate m1 m2), (TranslateRotate m1 m2), and
+ * (Concatenate m1 m2) built-in commands. Although these do exactly the
+ * same thing, using the appropriate command is recommended in order to
+ * explicitly describe the sequence of transforms and their intent.
+ *
+ * @param elements ArrayList containing Matrix4d objects m1 and m2 at
+ * indices 1 and 2 respectively
+ *
+ * @return matrix that concatenates m1 and m2 in that order: if a point is
+ * transformed by the resulting matrix, then in effect the points are
+ * first transformed by m1 and then m2
+ */
+ private Matrix4d concatenate(ArrayList elements) {
+ String functionName = (String)elements.get(0) ;
+
+ if (elements.size() != 3) {
+ throw new IllegalArgumentException
+ ("Incorrect number of arguments to " + functionName) ;
+ }
+
+ if (!(elements.get(1) instanceof Matrix4d) ||
+ !(elements.get(2) instanceof Matrix4d)) {
+ throw new IllegalArgumentException
+ ("Both arguments to " + functionName + " must be Matrix4d") ;
+ }
+
+ // Multiply the matrices in the order such that the result, when
+ // transforming a 3D point, will apply the transform represented by
+ // the 1st matrix and then apply the transform represented by the 2nd
+ // matrix.
+ Matrix4d m4d = new Matrix4d((Matrix4d)elements.get(2)) ;
+ m4d.mul((Matrix4d)elements.get(1)) ;
+
+ return m4d ;
+ }
+
+ /**
+ * Processes the built-in command (BoundingSphere center radius).
+ * This is used when configuring behaviors.
+ *
+ * @param elements ArrayList containing Point3d at index 1 for the sphere
+ * center and Double at index 2 wrapping the sphere radius, or the String
+ * "infinite" at index 2.
+ *
+ * @return BoundingSphere with the given center and radius
+ */
+ private BoundingSphere makeBoundingSphere(ArrayList elements) {
+ if (elements.size() != 3) {
+ throw new IllegalArgumentException
+ ("Incorrect number of arguments to BoundingSphere") ;
+ }
+
+ if (! (elements.get(1) instanceof Point3d) ||
+ ! (elements.get(2) instanceof Double ||
+ elements.get(2) instanceof String))
+ throw new IllegalArgumentException
+ ("BoundingSphere needs a Point3d center " +
+ "followed by a Double radius or the String \"infinite\"") ;
+
+ double r ;
+ if (elements.get(2) instanceof Double)
+ r = ((Double)elements.get(2)).doubleValue() ;
+ else
+ r = Double.POSITIVE_INFINITY ;
+
+ return new BoundingSphere((Point3d)elements.get(1), r) ;
+ }
+
+ void print() {
+ System.out.print("(") ;
+ int argc = elements.size() ;
+ for (int i = 0 ; i < argc ; i++) {
+ if (elements.get(i) instanceof Matrix3d) {
+ String[] rows = ConfigCommand.formatMatrixRows
+ ((Matrix3d)elements.get(i)) ;
+ System.out.println("\n ((" + rows[0] + ")") ;
+ System.out.println(" (" + rows[1] + ")") ;
+ System.out.print(" (" + rows[2] + "))") ;
+ if (i != (argc - 1)) System.out.println() ;
+ }
+ else if (elements.get(i) instanceof Matrix4d) {
+ String[] rows = ConfigCommand.formatMatrixRows
+ ((Matrix4d)elements.get(i)) ;
+ System.out.println("\n ((" + rows[0] + ")") ;
+ System.out.println(" (" + rows[1] + ")") ;
+ System.out.println(" (" + rows[2] + ")") ;
+ System.out.print(" (" + rows[3] + "))") ;
+ if (i != (argc - 1)) System.out.println() ;
+ }
+ else if (elements.get(i) instanceof ConfigSexpression) {
+ if (i > 0) System.out.print(" ") ;
+ ((ConfigSexpression)elements.get(i)).print() ;
+ if (i != (argc - 1)) System.out.println() ;
+ }
+ else if (elements.get(i) instanceof ConfigCommand) {
+ if (i > 0) System.out.print(" ") ;
+ System.out.print(elements.get(i).toString()) ;
+ if (i != (argc - 1)) System.out.println() ;
+ }
+ else {
+ if (i > 0) System.out.print(" ") ;
+ System.out.print(elements.get(i).toString()) ;
+ }
+ }
+ System.out.print(")") ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/universe/ConfigView.java b/src/classes/share/com/sun/j3d/utils/universe/ConfigView.java
new file mode 100644
index 0000000..7a3813c
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/ConfigView.java
@@ -0,0 +1,469 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.universe ;
+
+import java.util.* ;
+import javax.media.j3d.* ;
+import javax.vecmath.* ;
+
+class ConfigView extends ConfigObject {
+ /**
+ * The corresponding View and Viewer instances. These are set when
+ * createJ3dView() is called after parsing the configuration file.
+ */
+ View j3dView = null ;
+ Viewer j3dViewer = null ;
+
+ /**
+ * Set of ConfigScreen instances added to this view.
+ */
+ Set screens = new HashSet() ;
+
+ /**
+ * Indicates whether or not stereo viewing should be enabled for this
+ * ConfigView. This is set during parsing of the configuration file.
+ */
+ boolean stereoEnable = false ;
+
+ /**
+ * Indicates whether or not antialiasing is enabled for this ConfigView.
+ * This is set during parsing of the configuration file.
+ */
+ boolean antialiasingEnable = false;
+
+ /**
+ * Reference to the PhysicalBody associated with this ConfigView. This is
+ * set when createJ3dView() is called after parsing the configuration
+ * file.
+ */
+ PhysicalBody physicalBody = null ;
+
+ /**
+ * Reference to the PhysicalEnvironment associated with this ConfigView.
+ * This is set when createJ3dView() is called after parsing the
+ * configuration file.
+ */
+ PhysicalEnvironment physicalEnvironment = null ;
+
+ // All other configurable attributes.
+ private double fieldOfView = Math.PI/4.0 ;
+ private int backClipPolicy = View.PHYSICAL_EYE ;
+ private int frontClipPolicy = View.PHYSICAL_EYE ;
+ private double backClipDistance = 10.0 ;
+ private double frontClipDistance = 0.1 ;
+ private int screenScalePolicy = View.SCALE_SCREEN_SIZE ;
+ private double screenScale = 1.0 ;
+ private boolean trackingEnable = false ;
+ private int viewPolicy = View.SCREEN_VIEW ;
+ private int windowEyepointPolicy = -1 ;
+ private int windowMovementPolicy = -1 ;
+ private int windowResizePolicy = -1 ;
+ private boolean coeCenteringEnableSet = false ;
+ private boolean coeCenteringEnable = false ;
+ private Point3d centerEyeInCoexistence = null ;
+
+ private ConfigPhysicalBody configBody = null ;
+ private ConfigPhysicalEnvironment configEnv = null ;
+ private ConfigViewPlatform configViewPlatform = null ;
+
+ /**
+ * Overrides initialize() to do nothing.
+ */
+ protected void initialize(ConfigCommand command) {
+ }
+
+ /**
+ * Processes properties for this object. Handles commands of the form:<p>
+ * (ViewAttribute {instanceName} {attrName} {attrValue})
+ *
+ * @param command the command that invoked this method
+ */
+ protected void setProperty(ConfigCommand command) {
+
+ int argc = command.argc ;
+ Object[] argv = command.argv ;
+ String attr = null ;
+ Object val = null ;
+ String sval = null ;
+ ConfigScreen cs = null ;
+
+ // Check that arg[1] and arg[2] are strings
+ if (argc != 4) {
+ syntaxError("Incorrect number of arguments to " +
+ command.commandName) ;
+ }
+
+ if (!isName(argv[1])) {
+ syntaxError("The first argument to " + command.commandName +
+ " must be the instance name") ;
+ }
+
+ if (!isName(argv[2])) {
+ syntaxError("The second argument to " + command.commandName +
+ " must be a property name") ;
+ }
+
+ attr = (String) argv[2] ;
+ val = argv[3] ;
+
+ if (attr.equals("Screen") || attr.equals("Window")) {
+ if (!(val instanceof String)) {
+ syntaxError("Value for " + attr + " must be a name") ;
+ }
+ cs = (ConfigScreen)
+ configContainer.findConfigObject("Screen", (String)val) ;
+
+ if (!screens.add(cs)) {
+ syntaxError(attr + " \"" + ((String)val) +
+ "\" has already been added to " + instanceName) ;
+ }
+ }
+ else if (attr.equals("ViewPlatform")) {
+ if (!(val instanceof String)) {
+ syntaxError("value for ViewPlatform " +
+ " must be an instance name") ;
+ }
+ configViewPlatform =
+ (ConfigViewPlatform)configContainer.findConfigObject
+ ("ViewPlatform", (String)val) ;
+
+ configViewPlatform.addConfigView(this) ;
+ }
+ else if (attr.equals("PhysicalEnvironment")) {
+ if (!(val instanceof String)) {
+ syntaxError("value for PhysicalEnvironment " +
+ "must be an instance name") ;
+ }
+ configEnv =
+ (ConfigPhysicalEnvironment)configContainer.findConfigObject
+ ("PhysicalEnvironment", (String)val) ;
+ }
+ else if (attr.equals("PhysicalBody")) {
+ if (!(val instanceof String)) {
+ syntaxError("value for PhysicalBody " +
+ "must be an instance name") ;
+ }
+ configBody = (ConfigPhysicalBody)
+ configContainer.findConfigObject("PhysicalBody", (String)val) ;
+ }
+ else if (attr.equals("BackClipPolicy")) {
+ if (!(val instanceof String)) {
+ syntaxError("value for BackClipPolicy must be a string") ;
+ }
+ sval = (String) val ;
+ if (sval.equals("PHYSICAL_EYE"))
+ backClipPolicy = View.PHYSICAL_EYE ;
+ else if (sval.equals("PHYSICAL_SCREEN"))
+ backClipPolicy = View.PHYSICAL_SCREEN ;
+ else if (sval.equals("VIRTUAL_EYE"))
+ backClipPolicy = View.VIRTUAL_EYE ;
+ else if (sval.equals("VIRTUAL_SCREEN"))
+ backClipPolicy = View.VIRTUAL_SCREEN ;
+ else
+ syntaxError("Invalid value for BackClipPolicy " + sval) ;
+ }
+ else if (attr.equals("FrontClipPolicy")) {
+ if (!(val instanceof String)) {
+ syntaxError("value for FrontClipPolicy must be a string") ;
+ }
+ sval = (String) val ;
+ if (sval.equals("PHYSICAL_EYE"))
+ frontClipPolicy = View.PHYSICAL_EYE ;
+ else if (sval.equals("PHYSICAL_SCREEN"))
+ frontClipPolicy = View.PHYSICAL_SCREEN ;
+ else if (sval.equals("VIRTUAL_EYE"))
+ frontClipPolicy = View.VIRTUAL_EYE ;
+ else if (sval.equals("VIRTUAL_SCREEN"))
+ frontClipPolicy = View.VIRTUAL_SCREEN ;
+ else
+ syntaxError("Invalid value for FrontClipPolicy " + sval) ;
+ }
+ else if (attr.equals("ScreenScalePolicy")) {
+ if (!(val instanceof String)) {
+ syntaxError("value for ScreenScalePolicy must be a string") ;
+ }
+ sval = (String) val ;
+ if (sval.equals("SCALE_SCREEN_SIZE"))
+ screenScalePolicy = View.SCALE_SCREEN_SIZE ;
+ else if (sval.equals("SCALE_EXPLICIT"))
+ screenScalePolicy = View.SCALE_EXPLICIT ;
+ else
+ syntaxError("Invalid value for ScreenScalePolicy " + sval) ;
+ }
+ else if (attr.equals("FieldOfView")) {
+ if (!(val instanceof Double)) {
+ syntaxError("value for FieldOfView must be a number") ;
+ }
+ fieldOfView = ((Double)val).doubleValue() ;
+ }
+ else if (attr.equals("BackClipDistance")) {
+ if (!(val instanceof Double)) {
+ syntaxError("value for BackClipDistance must be a number") ;
+ }
+ backClipDistance = ((Double)val).doubleValue() ;
+ }
+ else if (attr.equals("FrontClipDistance")) {
+ if (!(val instanceof Double)) {
+ syntaxError("value for FrontClipDistance must be a number") ;
+ }
+ frontClipDistance = ((Double)val).doubleValue() ;
+ }
+ else if (attr.equals("ScreenScale")) {
+ if (!(val instanceof Double)) {
+ syntaxError("value for ScreenScale must be a number") ;
+ }
+ screenScale = ((Double)val).doubleValue() ;
+ }
+ else if (attr.equals("TrackingEnable")) {
+ if (!(val instanceof Boolean)) {
+ syntaxError("value for TrackingEnable must be a boolean") ;
+ }
+ trackingEnable = ((Boolean)val).booleanValue() ;
+ }
+ else if (attr.equals("CoexistenceCenteringEnable")) {
+ if (!(val instanceof Boolean)) {
+ syntaxError("value for CoexistenceCenteringEnable " +
+ "must be a boolean") ;
+ }
+ coeCenteringEnable = ((Boolean)val).booleanValue() ;
+ coeCenteringEnableSet = true ;
+ }
+ else if (attr.equals("ViewPolicy")) {
+ if (!(val instanceof String)) {
+ syntaxError("value for ViewPolicy must be a string") ;
+ }
+ sval = (String) val ;
+ if (sval.equals("SCREEN_VIEW"))
+ viewPolicy = View.SCREEN_VIEW ;
+ else if (sval.equals("HMD_VIEW"))
+ viewPolicy = View.HMD_VIEW ;
+ else
+ syntaxError("Invalid value for ViewPolicy " + sval) ;
+ }
+ else if (attr.equals("WindowEyepointPolicy")) {
+ if (!(val instanceof String)) {
+ syntaxError("value for WindowEyepointPolicy " +
+ "must be a string") ;
+ }
+ sval = (String) val ;
+ if (sval.equals("RELATIVE_TO_SCREEN"))
+ windowEyepointPolicy = View.RELATIVE_TO_SCREEN ;
+ else if (sval.equals("RELATIVE_TO_COEXISTENCE"))
+ windowEyepointPolicy = View.RELATIVE_TO_COEXISTENCE ;
+ else if (sval.equals("RELATIVE_TO_WINDOW"))
+ windowEyepointPolicy = View.RELATIVE_TO_WINDOW ;
+ else if (sval.equals("RELATIVE_TO_FIELD_OF_VIEW"))
+ windowEyepointPolicy = View.RELATIVE_TO_FIELD_OF_VIEW ;
+ else
+ syntaxError("Invalid value for WindowEyepointPolicy " + sval) ;
+ }
+ else if (attr.equals("WindowMovementPolicy")) {
+ if (!(val instanceof String)) {
+ syntaxError("value for WindowEyeMovementPolicy " +
+ "must be a string") ;
+ }
+ sval = (String) val ;
+ if (sval.equals("VIRTUAL_WORLD"))
+ windowMovementPolicy = View.VIRTUAL_WORLD ;
+ else if (sval.equals("PHYSICAL_WORLD"))
+ windowMovementPolicy = View.PHYSICAL_WORLD ;
+ else
+ syntaxError("Invalid value for WindowMovementPolicy " + sval) ;
+ }
+ else if (attr.equals("WindowResizePolicy")) {
+ if (!(val instanceof String)) {
+ syntaxError("value for WindowResizePolicy " +
+ "must be a string") ;
+ }
+ sval = (String) val ;
+ if (sval.equals("VIRTUAL_WORLD"))
+ windowResizePolicy = View.VIRTUAL_WORLD ;
+ else if (sval.equals("PHYSICAL_WORLD"))
+ windowResizePolicy = View.PHYSICAL_WORLD ;
+ else
+ syntaxError("Invalid value for WindowResizePolicy " + sval) ;
+ }
+ else if (attr.equals("CenterEyeInCoexistence")) {
+ if (val instanceof Point3d)
+ centerEyeInCoexistence = (Point3d)val ;
+ else
+ syntaxError("value for CenterEyeInCoexistence " +
+ "must be a Point3d") ;
+ }
+ else if (attr.equals("StereoEnable")) {
+ if (!(val instanceof Boolean)) {
+ syntaxError("value for StereoEnable must be a boolean") ;
+ }
+ stereoEnable = ((Boolean)val).booleanValue() ;
+ }
+ else if (attr.equals("AntialiasingEnable")) {
+ if (!(val instanceof Boolean)) {
+ syntaxError("value for AntialiasingEnable must be a boolean") ;
+ }
+ antialiasingEnable = ((Boolean)val).booleanValue() ;
+ }
+ else {
+ syntaxError("Unknown " + command.commandName +
+ " \"" + attr + "\"") ;
+ }
+ }
+
+ /**
+ * Create a core Java 3D View instance and a utility Viewer instance using
+ * the attributes gathered by this object.
+ */
+ protected Viewer createViewer(boolean setVisible) {
+ Point3d leftEyeCoe, rightEyeCoe ;
+
+ j3dView = new View() ;
+ j3dView.setViewPolicy(viewPolicy) ;
+
+ if (configBody == null)
+ physicalBody = new PhysicalBody() ;
+ else
+ physicalBody = configBody.j3dPhysicalBody ;
+
+ if (configEnv == null)
+ physicalEnvironment = new PhysicalEnvironment() ;
+ else
+ physicalEnvironment = configEnv.j3dPhysicalEnvironment ;
+
+ j3dView.setPhysicalBody(physicalBody) ;
+ j3dView.setPhysicalEnvironment(physicalEnvironment) ;
+
+ boolean standardDefaults = true ;
+ if (coeCenteringEnableSet && !coeCenteringEnable) {
+ standardDefaults = false ;
+ }
+ if (configEnv != null && configEnv.coexistenceToTrackerBase != null) {
+ standardDefaults = false ;
+ }
+ else {
+ Iterator i = screens.iterator() ;
+ while (i.hasNext()) {
+ ConfigScreen s = (ConfigScreen)i.next() ;
+ if (s.trackerBaseToImagePlate != null) {
+ standardDefaults = false ;
+ break ;
+ }
+ }
+ }
+
+ if (standardDefaults) {
+ // Coexistence centering has not been explicitly set false, and
+ // the tracker base to image plate and coexistence to tracker base
+ // transforms are unset, so use the standard Java 3D defaults.
+ if (windowEyepointPolicy == -1)
+ windowEyepointPolicy = View.RELATIVE_TO_FIELD_OF_VIEW ;
+ if (windowMovementPolicy == -1)
+ windowMovementPolicy = View.PHYSICAL_WORLD ;
+ if (windowResizePolicy == -1)
+ windowResizePolicy = View.PHYSICAL_WORLD ;
+ if (!coeCenteringEnableSet)
+ coeCenteringEnable = true ;
+ }
+ else {
+ // Use multi-screen or calibrated coexistence defaults.
+ if (windowEyepointPolicy == -1)
+ windowEyepointPolicy = View.RELATIVE_TO_COEXISTENCE ;
+ if (windowMovementPolicy == -1)
+ windowMovementPolicy = View.VIRTUAL_WORLD ;
+ if (windowResizePolicy == -1)
+ windowResizePolicy = View.VIRTUAL_WORLD ;
+ if (!coeCenteringEnableSet)
+ coeCenteringEnable = false ;
+ }
+
+ j3dView.setWindowEyepointPolicy(windowEyepointPolicy) ;
+ j3dView.setWindowMovementPolicy(windowMovementPolicy) ;
+ j3dView.setWindowResizePolicy(windowResizePolicy) ;
+ j3dView.setCoexistenceCenteringEnable(coeCenteringEnable) ;
+
+ if (centerEyeInCoexistence == null) {
+ centerEyeInCoexistence = new Point3d(0.0, 0.0, 0.4572) ;
+ }
+
+ leftEyeCoe = new Point3d(centerEyeInCoexistence) ;
+ rightEyeCoe = new Point3d(centerEyeInCoexistence) ;
+
+ if (stereoEnable) {
+ Point3d leftEyeBody = new Point3d() ;
+ Point3d rightEyeBody = new Point3d() ;
+
+ physicalBody.getLeftEyePosition(leftEyeBody) ;
+ physicalBody.getRightEyePosition(rightEyeBody) ;
+
+ leftEyeCoe.add(leftEyeBody) ;
+ rightEyeCoe.add(rightEyeBody) ;
+ }
+
+ j3dView.setLeftManualEyeInCoexistence(leftEyeCoe) ;
+ j3dView.setRightManualEyeInCoexistence(rightEyeCoe) ;
+
+ j3dView.setBackClipPolicy(backClipPolicy) ;
+ j3dView.setFrontClipPolicy(frontClipPolicy) ;
+ j3dView.setBackClipDistance(backClipDistance) ;
+ j3dView.setFrontClipDistance(frontClipDistance) ;
+
+ j3dView.setScreenScalePolicy(screenScalePolicy) ;
+ j3dView.setScreenScale(screenScale) ;
+
+ j3dView.setFieldOfView(fieldOfView) ;
+ j3dView.setTrackingEnable(trackingEnable) ;
+ j3dView.setSceneAntialiasingEnable(antialiasingEnable) ;
+
+ if (screens.size() == 0) {
+ throw new IllegalStateException
+ (errorMessage(creatingCommand, "View \"" + instanceName +
+ "\" has no canvases or screens")) ;
+ }
+
+ ConfigScreen[] cs = new ConfigScreen[screens.size()] ;
+ screens.toArray(cs) ;
+
+ j3dViewer = new Viewer(cs, this, setVisible) ;
+ return j3dViewer ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/universe/ConfigViewPlatform.java b/src/classes/share/com/sun/j3d/utils/universe/ConfigViewPlatform.java
new file mode 100644
index 0000000..2ce5070
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/ConfigViewPlatform.java
@@ -0,0 +1,255 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.universe ;
+
+import java.util.ArrayList ;
+import javax.media.j3d.Node ;
+import javax.media.j3d.View ;
+import javax.media.j3d.ViewPlatform ;
+import javax.media.j3d.Transform3D ;
+import javax.media.j3d.TransformGroup ;
+import javax.vecmath.Matrix4d ;
+import com.sun.j3d.utils.behaviors.vp.ViewPlatformBehavior ;
+
+class ConfigViewPlatform extends ConfigObject {
+
+ private boolean allowPolicyRead = false ;
+ private boolean allowLocalToVworldRead = false ;
+ private boolean nominalViewingTransform = false ;
+ private Transform3D initialViewingTransform = null ;
+ private ArrayList configViews = new ArrayList() ;
+ private Viewer[] viewers = null ;
+
+ /**
+ * The corresponding ViewingPlatform instance.
+ */
+ ViewingPlatform viewingPlatform = null ;
+
+ /**
+ * Indicates the view attach policy specified in the configuration file.
+ * If none is set, it remains -1 even though a default may be in effect.
+ */
+ int viewAttachPolicy = -1 ;
+
+ /**
+ * The associated ConfigViewPlatformBehavior, if any.
+ */
+ ConfigViewPlatformBehavior configBehavior = null ;
+
+ /**
+ * Overrides initialize() to do nothing.
+ */
+ protected void initialize(ConfigCommand command) {
+ }
+
+ /**
+ * Processes attributes for this object. Handles commands of the form:<p>
+ * (ViewPlatformAttribute {instanceName} {attrName} {attrValue})<br>
+ * (ViewPlatformProperty {instanceName} {attrName} {attrValue})
+ *
+ * @param command the command that invoked this method
+ */
+ protected void setProperty(ConfigCommand command) {
+
+ int argc = command.argc ;
+ Object[] argv = command.argv ;
+ String attribute ;
+ Object value ;
+
+ if (argc != 4) {
+ syntaxError("Incorrect number of arguments to " +
+ command.commandName) ;
+ }
+
+ if (!isName(argv[2])) {
+ syntaxError("The second argument to " + command.commandName +
+ " must be a property name");
+ }
+
+ attribute = (String)argv[2] ;
+ value = argv[3] ;
+
+ if (attribute.equals("NominalViewingTransform")) {
+ if (! (value instanceof Boolean)) {
+ syntaxError("NominalViewingTransform must be a boolean") ;
+ }
+ nominalViewingTransform = ((Boolean)value).booleanValue() ;
+ }
+ else if (attribute.equals("InitialViewingTransform")) {
+ if (! (value instanceof Matrix4d)) {
+ syntaxError("InitialViewingTransform must be a Matrix4d") ;
+ }
+ initialViewingTransform = new Transform3D((Matrix4d)value) ;
+ }
+ else if (attribute.equals("ViewAttachPolicy")) {
+ if (! (value instanceof String)) {
+ syntaxError("ViewAttachPolicy must be a string") ;
+ }
+
+ String svalue = (String)value ;
+
+ if (svalue.equals("NOMINAL_HEAD"))
+ viewAttachPolicy = View.NOMINAL_HEAD ;
+ else if (svalue.equals("NOMINAL_SCREEN"))
+ viewAttachPolicy = View.NOMINAL_SCREEN ;
+ else if (svalue.equals("NOMINAL_FEET"))
+ viewAttachPolicy = View.NOMINAL_FEET ;
+ else
+ syntaxError("Illegal value " +
+ svalue + " for ViewAttachPolicy") ;
+ }
+ else if (attribute.equals("ViewPlatformBehavior")) {
+ if (! (value instanceof String)) {
+ syntaxError("ViewPlatformBehavior must be a name") ;
+ }
+ configBehavior =
+ (ConfigViewPlatformBehavior)configContainer.findConfigObject
+ ("ViewPlatformBehavior", (String)value) ;
+ }
+ else if (attribute.equals("AllowPolicyRead")) {
+ if (!(value instanceof Boolean)) {
+ syntaxError("value for AllowPolicyRead " +
+ "must be a boolean") ;
+ }
+ allowPolicyRead = ((Boolean)value).booleanValue() ;
+ }
+ else if (attribute.equals("AllowLocalToVworldRead")) {
+ if (!(value instanceof Boolean)) {
+ syntaxError("value for AllowLocalToVworldRead " +
+ "must be a boolean") ;
+ }
+ allowLocalToVworldRead = ((Boolean)value).booleanValue() ;
+ }
+ else {
+ syntaxError("Unknown " + command.commandName +
+ " \"" + attribute + "\"") ;
+ }
+ }
+
+ /**
+ * Add a ConfigView to this ConfigViewPlatform.
+ */
+ void addConfigView(ConfigView cv) {
+ configViews.add(cv) ;
+ }
+
+ /**
+ * Creates a ViewingPlatform from attributes gathered by this object.
+ *
+ * @param transformCount the number of TransformGroups to attach to the
+ * ViewingPlatform
+ * @return the new ViewingPlatform
+ */
+ ViewingPlatform createViewingPlatform(int transformCount) {
+
+ // Get the Viewers attached to this ViewingPlatform.
+ // All ConfigViews must be processed at this point.
+ if (configViews.size() == 0) {
+ viewers = new Viewer[0] ;
+ }
+ else {
+ viewers = new Viewer[configViews.size()] ;
+ for (int i = 0 ; i < viewers.length ; i++)
+ viewers[i] = ((ConfigView)configViews.get(i)).j3dViewer ;
+ }
+
+ // Create the viewing platform and get its ViewPlatform instance.
+ viewingPlatform = new ViewingPlatform(transformCount) ;
+ ViewPlatform vp = viewingPlatform.getViewPlatform() ;
+
+ // Set defined policies.
+ if (allowPolicyRead)
+ vp.setCapability(ViewPlatform.ALLOW_POLICY_READ) ;
+
+ if (allowLocalToVworldRead)
+ vp.setCapability(Node.ALLOW_LOCAL_TO_VWORLD_READ) ;
+
+ if (viewAttachPolicy == -1) {
+ // Apply a default based on the eyepoint policy.
+ boolean nominalHead = true ;
+ for (int i = 0 ; i < viewers.length ; i++) {
+ if (viewers[i].getView().getWindowEyepointPolicy() !=
+ View.RELATIVE_TO_FIELD_OF_VIEW) {
+ nominalHead = false ;
+ break ;
+ }
+ }
+ if (nominalHead)
+ vp.setViewAttachPolicy(View.NOMINAL_HEAD) ;
+ else
+ vp.setViewAttachPolicy(View.NOMINAL_SCREEN) ;
+ }
+ else {
+ vp.setViewAttachPolicy(viewAttachPolicy) ;
+ }
+
+ // Assign the viewing platform to all viewers.
+ for (int i = 0 ; i < viewers.length ; i++) {
+ viewers[i].setViewingPlatform(viewingPlatform) ;
+ }
+
+ // Apply initial viewing transforms if defined.
+ if (nominalViewingTransform) {
+ viewingPlatform.setNominalViewingTransform() ;
+ }
+
+ if (initialViewingTransform != null) {
+ TransformGroup tg = viewingPlatform.getViewPlatformTransform() ;
+ tg.setTransform(initialViewingTransform) ;
+ }
+
+ return viewingPlatform ;
+ }
+
+ /**
+ * Attach any ViewPlatformBehavior specified for this platform.
+ */
+ void processBehavior() {
+ if (configBehavior != null) {
+ viewingPlatform.setViewPlatformBehavior
+ (configBehavior.viewPlatformBehavior) ;
+ }
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/universe/ConfigViewPlatformBehavior.java b/src/classes/share/com/sun/j3d/utils/universe/ConfigViewPlatformBehavior.java
new file mode 100644
index 0000000..944e15f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/ConfigViewPlatformBehavior.java
@@ -0,0 +1,139 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.universe ;
+
+import java.lang.reflect.* ;
+import java.util.ArrayList ;
+import javax.vecmath.Matrix4d ;
+import javax.media.j3d.Bounds ;
+import javax.media.j3d.Canvas3D ;
+import javax.media.j3d.Sensor ;
+import javax.media.j3d.Transform3D ;
+import com.sun.j3d.utils.behaviors.vp.ViewPlatformBehavior ;
+
+class ConfigViewPlatformBehavior extends ConfigObject {
+
+ // All known configurable properties.
+ private Transform3D homeTransform = null ;
+ private Bounds schedulingBounds = null ;
+ private int schedulingInterval = -1 ;
+
+ /**
+ * The corresponding ViewPlatformBehavior instance.
+ */
+ ViewPlatformBehavior viewPlatformBehavior ;
+
+ /**
+ * Processes properties for this object. Handles commands of the form:<p>
+ * (ViewPlatformBehaviorProperty {instanceName} {attrName} {arg} ...)
+ *
+ * @param command the command that invoked this method
+ */
+ protected void setProperty(ConfigCommand cmd) {
+
+ int argc = cmd.argc ;
+ Object[] argv = cmd.argv ;
+
+ if (argc < 4) {
+ syntaxError("Wrong number of arguments to " + cmd.commandName) ;
+ }
+
+ if (! isName(argv[2])) {
+ syntaxError("The second argument to " + cmd.commandName +
+ " must be a property name");
+ }
+
+ String attribute = (String)argv[2] ;
+ if (attribute.equals("HomeTransform")) {
+ if (! (argv[3] instanceof Matrix4d)) {
+ syntaxError("HomeTransform must be a Matrix4d") ;
+ }
+ homeTransform = new Transform3D((Matrix4d)argv[3]) ;
+ }
+ else if (attribute.equals("SchedulingBounds")) {
+ if (! (argv[3] instanceof Bounds)) {
+ syntaxError("SchedulingBounds must be an instance of Bounds") ;
+ }
+ schedulingBounds = (Bounds)argv[3] ;
+ }
+ else if (attribute.equals("SchedulingInterval")) {
+ if (! (argv[3] instanceof Double)) {
+ syntaxError("SchedulingInterval must be a priority (number)") ;
+ }
+ schedulingInterval = ((Double)argv[3]).intValue() ;
+ }
+ else {
+ // It's not any of the pre-defined attributes. Add it to the
+ // properties list for the behavior instance itself to evaluate.
+ properties.add(cmd) ;
+ }
+ }
+
+ /**
+ * Instantiate a ViewPlatformBehavior of the given class name.<p>
+ *
+ * NOTE: All ConfigView and ConfigSensor objects must be processed before
+ * calling this method.
+ *
+ * @return the configured ViewPlatformBehavior, or null if error
+ */
+ ViewPlatformBehavior createViewPlatformBehavior() {
+
+ viewPlatformBehavior = (ViewPlatformBehavior)createTargetObject() ;
+
+ // Set known attributes.
+ if (homeTransform != null)
+ viewPlatformBehavior.setHomeTransform(homeTransform) ;
+
+ if (schedulingBounds != null)
+ viewPlatformBehavior.setSchedulingBounds(schedulingBounds) ;
+
+ if (schedulingInterval != -1)
+ viewPlatformBehavior.setSchedulingInterval(schedulingInterval) ;
+
+ // Unknown properties in the concrete instance are evaluated later.
+ return viewPlatformBehavior ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/universe/ConfiguredUniverse.java b/src/classes/share/com/sun/j3d/utils/universe/ConfiguredUniverse.java
new file mode 100644
index 0000000..52d9c65
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/ConfiguredUniverse.java
@@ -0,0 +1,768 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.universe;
+
+import java.net.URL;
+import java.net.MalformedURLException;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import javax.media.j3d.*;
+
+/**
+ * This utility class creates all the necessary objects on the view side of
+ * the scene graph. Specifically, it creates a Locale, one or more
+ * ViewingPlatforms, and at least one Viewer object.<p>
+ *
+ * ConfiguredUniverse can set up a viewing environment based upon the contents
+ * of a configuration file. This allows an application to run without change
+ * across a broad range of viewing configurations, such as windows on
+ * conventional desktops, stereo-enabled views, full screen immersive displays
+ * on single or multiple screens, or virtual reality installations including
+ * cave and head-mounted displays incorporating 6 degree of freedom sensor
+ * devices.<p>
+ *
+ * A configuration file may create InputDevice, Sensor, and
+ * ViewPlatformBehavior instances as well as Viewers and ViewingPlatforms. At
+ * least one Viewer must be provided by the configuration. If a
+ * ViewingPlatform is not provided, a default one will be created and the
+ * Viewer will be attached to it.<p>
+ *
+ * A configuration file may be specified directly by passing a URL to a
+ * ConfiguredUniverse constructor. Alternatively, a ConfigContainer may be
+ * created from a configuration file first, and then passed to an appropriate
+ * ConfiguredUniverse constructor. The latter technique allows Java system
+ * properties that affect Java 3D to be specified in the configuration file,
+ * as long as no references to a VirtualUniverse are made before creating the
+ * container.<p>
+ *
+ * If a configuration file or container is not provided, then
+ * ConfiguredUniverse creates a default viewing environment in the same way as
+ * SimpleUniverse. If one or more Canvas3D objects are provided, it will use
+ * them instead of creating new ones. All of the constructors provided by
+ * SimpleUniverse are also available here.<p>
+ *
+ * The syntax and description of the configuration file may be found
+ * <A href="doc-files/config-syntax.html">here.</a> Example config files can
+ * be found <A href="doc-files/config-examples.html">here.</a>
+ *
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ * @see ConfigContainer
+ * @see <a href="doc-files/config-syntax.html">
+ * The Java 3D Configuration File</a>
+ * @see <a href="doc-files/config-examples.html">
+ * Example Configuration Files</a>
+ *
+ * @since Java 3D 1.3
+ */
+public class ConfiguredUniverse extends SimpleUniverse {
+
+ /**
+ * The configuration instance for this universe.
+ */
+ private ConfigContainer configContainer = null;
+
+ /**
+ * Equivalent to <code>SimpleUniverse()</code>. Creates a
+ * Locale, a single ViewingPlatform, and a Viewer object.
+ *
+ * @see SimpleUniverse#SimpleUniverse()
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ */
+ public ConfiguredUniverse() {
+ super();
+ }
+
+ /**
+ * Equivalent to <code>SimpleUniverse(int)</code>.
+ * Creates a Locale, a single ViewingPlatform with the specified number of
+ * transforms, and a Viewer object.
+ *
+ * @param transformCount the number of transforms in the
+ * MultiTransformGroup object to be created
+ *
+ * @see SimpleUniverse#SimpleUniverse(int)
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ * @see MultiTransformGroup
+ */
+ public ConfiguredUniverse(int transformCount) {
+ super(transformCount);
+ }
+
+ /**
+ * Equivalent to <code>SimpleUniverse(Canvas3D)</code>.
+ * Creates a Locale, a single ViewingPlatform, and a Viewer object using
+ * the given Canvas3D instance.
+ *
+ * @param canvas the canvas to associate with the Viewer object;
+ * passing in null will cause this parameter to be ignored and a canvas
+ * to be created by the utility
+ *
+ * @see SimpleUniverse#SimpleUniverse(Canvas3D)
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ */
+ public ConfiguredUniverse(Canvas3D canvas) {
+ super(canvas);
+ }
+
+ /**
+ * Equivalent to <code>SimpleUniverse(Canvas3D, int)</code>.
+ * Creates a Locale, a single ViewingPlatform with the specified number of
+ * transforms, and a Viewer object with the given Canvas3D.
+ *
+ * @param canvas the canvas to associate with the Viewer object;
+ * passing in null will cause this parameter to be ignored and a canvas
+ * to be created by the utility
+ * @param transformCount the number of transforms in the
+ * MultiTransformGroup object to be created
+ *
+ * @see SimpleUniverse#SimpleUniverse(Canvas3D, int)
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ * @see MultiTransformGroup
+ */
+ public ConfiguredUniverse(Canvas3D canvas, int transformCount) {
+ super(canvas, transformCount);
+ }
+
+ /**
+ * Equivalent to <code>SimpleUniverse(ViewingPlatform, Viewer)</code>.
+ * Creates the view side of the scene graph with the given ViewingPlatform
+ * and Viewer.
+ *
+ * @param viewingPlatform the viewingPlatform to use to create
+ * the view side of the scene graph
+ * @param viewer the viewer object to use to create
+ * the view side of the scene graph
+ *
+ * @see SimpleUniverse#SimpleUniverse(ViewingPlatform, Viewer)
+ * @see ViewingPlatform
+ * @see Viewer
+ */
+ public ConfiguredUniverse(ViewingPlatform viewingPlatform, Viewer viewer) {
+ super(viewingPlatform, viewer, null);
+ }
+
+ /**
+ * Equivalent to <code>SimpleUniverse(ViewingPlatform, Viewer,
+ * LocalFactory)</code>. Creates the view side of the scene graph with
+ * the given ViewingPlatform, Viewer, and Locale created by the specified
+ * LocaleFactory.
+ *
+ * @param viewingPlatform the viewingPlatform to use to create
+ * the view side of the scene graph
+ * @param viewer the viewer object to use to create
+ * the view side of the scene graph
+ * @param localeFactory the factory object used to create the Locale
+ *
+ * @see SimpleUniverse#SimpleUniverse(ViewingPlatform, Viewer,
+ * LocaleFactory)
+ * @see ViewingPlatform
+ * @see Viewer
+ * @see LocaleFactory
+ */
+ public ConfiguredUniverse(ViewingPlatform viewingPlatform, Viewer viewer,
+ LocaleFactory localeFactory ) {
+ super(viewingPlatform, viewer, localeFactory);
+ }
+
+ /**
+ * Creates a Locale, a single ViewingPlatform, and a Viewer object from
+ * the given array of Canvas3D instances.
+ *
+ * @param canvases the canvases to associate with the Viewer object;
+ * passing in null will cause this parameter to be ignored and a canvas
+ * to be created by the utility
+ *
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ */
+ public ConfiguredUniverse(Canvas3D[] canvases) {
+ this(1, canvases, null, null, null, true);
+ }
+
+ /**
+ * Creates a Locale, a single ViewingPlatform with the specified number of
+ * transforms, and a Viewer object using the given array of Canvas3D
+ * instances.
+ *
+ * @param canvases the canvases to associate with the Viewer object;
+ * passing in null will cause this parameter to be ignored and a canvas
+ * to be created by the utility
+ * @param transformCount the number of transforms in the
+ * MultiTransformGroup object to be created
+ *
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ * @see MultiTransformGroup
+ */
+ public ConfiguredUniverse(Canvas3D[] canvases, int transformCount) {
+ this(transformCount, canvases, null, null, null, true);
+ }
+
+ /**
+ * Reads the configuration specified by the given URL to create a Locale,
+ * one or more ViewingPlatforms, and at least one Viewer object. The
+ * configuration file may also create InputDevice, Sensor, and
+ * ViewPlatformBehavior instances.
+ *
+ * @param userConfig the URL to the user's configuration file; passing in
+ * null creates a default Viewer and ViewingPlatform
+ *
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ */
+ public ConfiguredUniverse(URL userConfig) {
+ this(1, null, userConfig, null, null, true);
+ }
+
+ /**
+ * Reads the configuration specified by the given URL to create a Locale,
+ * one or more ViewingPlatforms with the specified number of transforms,
+ * and at least one Viewer object. The configuration file may also create
+ * InputDevice, Sensor, and ViewPlatformBehavior instances.
+ *
+ * @param userConfig the URL to the user's configuration file; passing in
+ * null creates a default Viewer and ViewingPlatform with the specified
+ * number of transforms
+ * @param transformCount the number of transforms in the
+ * MultiTransformGroup objects to be created
+ *
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ * @see MultiTransformGroup
+ */
+ public ConfiguredUniverse(URL userConfig, int transformCount) {
+ this(transformCount, null, userConfig, null, null, true);
+ }
+
+ /**
+ * Reads the configuration specified by the given URL to create a Locale,
+ * one or more ViewingPlatforms with the specified number of transforms,
+ * and at least one Viewer object with optional visibility. AWT
+ * components used by the Viewers will remain invisible unless the
+ * <code>setVisible</code> flag is true. The configuration file may also
+ * create InputDevice, Sensor, and ViewPlatformBehavior instances.
+ *
+ * @param userConfig the URL to the user's configuration file; passing in
+ * null creates a default Viewer with the specified visibility and a
+ * ViewingPlatform with the specified number of transforms
+ * @param transformCount the number of transforms in the
+ * MultiTransformGroup object to be created
+ * @param setVisible if true, calls <code>setVisible(true)</code> on all
+ * created window components; otherwise, they remain invisible
+ *
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ * @see MultiTransformGroup
+ */
+ public ConfiguredUniverse(URL userConfig,
+ int transformCount, boolean setVisible) {
+ this(transformCount, null, userConfig, null, null, setVisible);
+ }
+
+ /**
+ * Reads the configuration specified by the given URL to create a Locale
+ * using the given LocaleFactory, one or more ViewingPlatforms, and at
+ * least one Viewer object. The configuration file may also create
+ * InputDevice, Sensor, and ViewPlatformBehavior instances.
+ *
+ * @param userConfig the URL to the user's configuration file; passing in
+ * null creates a default Viewer and ViewingPlatform with the specified
+ * number of transforms
+ * @param localeFactory the factory object used to create the Locale
+ *
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ */
+ public ConfiguredUniverse(URL userConfig, LocaleFactory localeFactory) {
+ this(1, null, userConfig, localeFactory, null, true);
+ }
+
+ /**
+ * Reads the configuration specified by the given URL to create a Locale
+ * using the given LocaleFactory, one or more ViewingPlatforms, and at
+ * least one Viewer object with optional visibility. The configuration
+ * file may also create InputDevice, Sensor, and ViewPlatformBehavior
+ * instances. Window components used by the Viewers will remain invisible
+ * unless the <code>setVisible</code> flag is true.
+ *
+ * @param userConfig the URL to the user's configuration file; passing in
+ * null creates a default Viewer with the specified visibility and a
+ * default ViewingPlatform
+ * @param localeFactory the factory object used to create the Locale
+ * @param setVisible if true, calls <code>setVisible(true)</code> on all
+ * created window components; otherwise, they remain invisible
+ *
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ */
+ public ConfiguredUniverse(URL userConfig,
+ LocaleFactory localeFactory,
+ boolean setVisible) {
+ this(1, null, userConfig, localeFactory, null, setVisible);
+ }
+
+ /**
+ * Reads the configuration specified by the given URL to create a Locale
+ * using the specified LocaleFactory with the given origin, one or more
+ * ViewingPlatforms with the specified number of transforms, and at least
+ * one Viewer object with optional visibility. Window components used by
+ * the Viewers will remain invisible unless the <code>setVisible</code>
+ * flag is true. The configuration file may also create InputDevice,
+ * Sensor, and ViewPlatformBehavior instances.
+ *
+ * @param userConfig the URL to the user's configuration file; passing in
+ * null creates a default Viewer with the specified visibility and a
+ * ViewingPlatform with the specified number of transforms
+ * @param localeFactory the factory object used to create the Locale
+ * @param origin the origin used to set the origin of the Locale object;
+ * if this object is null, then 0.0 is used
+ * @param transformCount the number of transforms in the
+ * MultiTransformGroup object to be created
+ * @param setVisible if true, calls <code>setVisible(true)</code> on all
+ * created window components; otherwise, they remain invisible
+ *
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ * @see MultiTransformGroup
+ */
+ public ConfiguredUniverse(URL userConfig, LocaleFactory localeFactory,
+ HiResCoord origin, int transformCount,
+ boolean setVisible) {
+
+ this(transformCount, null, userConfig,
+ localeFactory, origin, setVisible);
+ }
+
+ /**
+ * Retrieves view-side scenegraph components from the given container to
+ * create a universe with one Locale, one or more ViewingPlatforms, and at
+ * least one Viewer object. Equivalent to
+ * <code>ConfiguredUniverse(ConfigContainer, null, null)</code>.
+ *
+ * @param userConfig container holding viewing configuration components;
+ * must not be null
+ *
+ * @see #ConfiguredUniverse(ConfigContainer, LocaleFactory, HiResCoord)
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ * @since Java 3D 1.3.1
+ */
+ public ConfiguredUniverse(ConfigContainer userConfig) {
+ this(userConfig, null, null);
+ }
+
+ /**
+ * Retrieves view-side scenegraph components from the given container to
+ * create a universe with one Locale created from the specified
+ * LocaleFactory and origin, one or more ViewingPlatforms, and at least
+ * one Viewer object. The container may also provide InputDevice, Sensor,
+ * and ViewPlatformBehavior instances which will be incorporated into the
+ * universe if they are referenced by any of the Viewer or ViewingPlatform
+ * instances.<p>
+ *
+ * This constructor and <code>ConfiguredUniverse(ConfigContainer)</code>
+ * both accept ConfigContainer references directly and are the preferred
+ * interfaces for constructing universes from configuration files. They
+ * differ from the constructors that accept URL objects in the
+ * following ways:<p>
+ * <ul>
+ * <li>A Viewer will be attached to a default ViewingPlatform only if
+ * no ViewingPlatforms are provided in the ConfigContainer. If one
+ * or more ViewingPlatforms are provided by the ConfigContainer, then
+ * Viewers must be attached to them explicitly in the configuration.<p>
+ * </li>
+ * <li>ViewPlatformBehaviors will be attached to their specified
+ * ViewingPlatforms before ConfiguredUniverse can set a reference to
+ * itself in the ViewingPlatform. This means that a behavior can't
+ * get a reference to the universe at the time its
+ * <code>setViewingPlatform</code> method is called; it must wait
+ * until its <code>initialize</code> method is called.<p>
+ * </li>
+ * <li>All Java properties used by Java 3D may be set in the beginning of
+ * the configuration file as long as there is no reference to a
+ * VirtualUniverse prior to creating the ConfigContainer. Note
+ * however, that some Java 3D utilities and objects such as
+ * Transform3D can cause static references to VirtualUniverse and
+ * trigger the evaluation of Java properties before they are set by
+ * ConfigContainer.<p>
+ * </li>
+ * </ul>
+ * @param userConfig container holding viewing configuration components;
+ * must not be null
+ * @param localeFactory the factory object used to create the Locale, or
+ * null
+ * @param origin the origin used to set the origin of the Locale object;
+ * if this object is null, then 0.0 is used
+ *
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ * @since Java 3D 1.3.1
+ */
+ public ConfiguredUniverse(ConfigContainer userConfig,
+ LocaleFactory localeFactory,
+ HiResCoord origin) {
+
+ super(origin, localeFactory);
+ configContainer = userConfig;
+
+ Collection c = configContainer.getViewers();
+ if (c == null || c.size() == 0)
+ throw new IllegalArgumentException
+ ("\nno views defined in configuration file\n");
+
+ viewer = (Viewer[])c.toArray(new Viewer[1]);
+
+ c = configContainer.getViewingPlatforms();
+ if (c == null || c.size() == 0) {
+ createDefaultViewingPlatform
+ (configContainer.getViewPlatformTransformCount());
+ }
+ else {
+ Iterator i = c.iterator();
+ while (i.hasNext()) {
+ ViewingPlatform vp = (ViewingPlatform)i.next();
+ vp.setUniverse(this);
+ locale.addBranchGraph(vp);
+ }
+ }
+ }
+
+ /**
+ * Package-scope constructor that creates the view side of the
+ * scene graph. The passed in parameters override the default
+ * values where appropriate. Note that the userCanvases parameter
+ * is ignored when the userConfig is non-null.
+ *
+ * @param transformCount the number of transforms in the
+ * MultiTransformGroup object to be created
+ * @param canvases the canvases to associate with the Viewer object;
+ * passing in null will cause this parameter to be ignored and a canvas
+ * to be created by the utility
+ * @param userConfig the URL to the user's configuration file; passing in
+ * null causes the default values to be used.
+ * @param localeFactory the factory object used to create the Locale
+ * @param origin the origin used to set the origin of the Locale object;
+ * if this object is null, then 0.0 is used
+ * @param setVisible if true, calls <code>setVisible(true)</code> on all
+ * created window components; otherwise, they remain invisible
+ *
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ * @see MultiTransformGroup
+ */
+ ConfiguredUniverse(int transformCount,
+ Canvas3D[] canvases,
+ URL userConfig,
+ LocaleFactory localeFactory,
+ HiResCoord origin,
+ boolean setVisible) {
+
+ super(origin, localeFactory);
+
+ if (userConfig == null) {
+ viewer = new Viewer[1];
+ viewer[0] = new Viewer(canvases, null, null, setVisible);
+ createDefaultViewingPlatform(transformCount);
+ }
+ else {
+ // Create a ConfigContainer without attaching behaviors. The
+ // package-scope constructor is used for backward compatibility.
+ configContainer = new ConfigContainer
+ (userConfig, setVisible, transformCount, false);
+
+ Collection c = configContainer.getViewers();
+ if (c == null || c.size() == 0)
+ throw new IllegalArgumentException
+ ("\nno views defined in configuration file\n");
+
+ viewer = (Viewer[])c.toArray(new Viewer[1]);
+
+ // Get ViewingPlatforms from the ConfigContainer and add them to
+ // the locale. The package-scoped findConfigObjects() accesor is
+ // used so that backward compatibility can be maintained for older
+ // configuration files.
+ c = configContainer.findConfigObjects("ViewPlatform");
+ if (c == null || c.size() == 0) {
+ createDefaultViewingPlatform(transformCount);
+ }
+ else {
+ Iterator i = c.iterator();
+ while (i.hasNext()) {
+ ConfigViewPlatform cvp = (ConfigViewPlatform)i.next();
+ ViewingPlatform vp = cvp.viewingPlatform;
+
+ // For backward compatibility, handle the default
+ // attachment of one Viewer to one ViewingPlatform. If
+ // there are multiple Viewers and ViewingPlatforms then
+ // attachments must be made explicitly in the config file.
+ if (vp.getViewers() == null &&
+ viewer.length == 1 && c.size() == 1) {
+ if (cvp.viewAttachPolicy == -1) {
+ setDerivedAttachPolicy(viewer[0], vp) ;
+ }
+ viewer[0].setViewingPlatform(vp);
+ }
+ vp.setUniverse(this);
+ locale.addBranchGraph(vp);
+
+ // If there's a behavior associated with the platform,
+ // attach it now after the setting the universe reference.
+ cvp.processBehavior();
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates a default ViewingPlatform, attaches the first Viewer, and then
+ * attaches the platform to the Locale.
+ *
+ * @param transformCount number of TransformGroups to create in the
+ * ViewingPlatform
+ */
+ private void createDefaultViewingPlatform(int transformCount) {
+ ViewingPlatform vp = new ViewingPlatform(transformCount);
+ setDerivedAttachPolicy(viewer[0], vp);
+ viewer[0].setViewingPlatform(vp);
+ vp.setUniverse(this);
+ locale.addBranchGraph(vp);
+ }
+
+ /**
+ * Sets a view attach policy appropriate for a window eyepoint policy.
+ *
+ * @param v Viewer to which the ViewingPlatform will be attached
+ * @param vp ViewingPlatform to which the Viewer will be attached
+ */
+ private void setDerivedAttachPolicy(Viewer v, ViewingPlatform vp) {
+ if (v.getView().getWindowEyepointPolicy() !=
+ View.RELATIVE_TO_FIELD_OF_VIEW) {
+ vp.getViewPlatform().setViewAttachPolicy(View.NOMINAL_SCREEN);
+ }
+ }
+
+
+ /**
+ * Returns the Viewer object specified by the given index.
+ *
+ * @param index The index of which Viewer object to return.
+ *
+ * @return The Viewer object specified by the given index.
+ */
+ public Viewer getViewer(int index) {
+ return viewer[index];
+ }
+
+ /**
+ * Returns all of the Viewer objects associated with this scene graph.
+ *
+ * @return The Viewer objects associated with this scene graph.
+ */
+ public Viewer[] getViewers() {
+ Viewer[] ret = new Viewer[viewer.length];
+ for (int i = 0; i < viewer.length; i++) {
+ ret[i] = viewer[i];
+ }
+ return ret;
+ }
+
+ /**
+ * Call <code>setVisible()</code> on all AWT components created by this
+ * ConfiguredUniverse instance.<p>
+ *
+ * @param visible boolean to be passed to the <code>setVisible()</code>
+ * calls on the window components created by this
+ * ConfiguredUniverse instance
+ */
+ public void setVisible(boolean visible) {
+ for (int i = 0; i < viewer.length; i++)
+ if (viewer[i] != null)
+ viewer[i].setVisible(visible);
+ }
+
+ /**
+ * Returns the config file URL based on system properties. This is
+ * equivalent to calling <code>ConfigContainer.getConfigURL()</code>. The
+ * current implementation of this method parses the j3d.configURL property
+ * as a URL string. For example, the following command line would specify
+ * that the config file is taken from the file "j3dconfig" in the current
+ * directory:
+ * <ul>
+ * <code>java -Dj3d.configURL=file:j3dconfig ...</code>
+ * </ul>
+ *
+ * @return the URL of the config file; null is returned if no valid
+ * URL is defined by the system properties
+ */
+ public static URL getConfigURL() {
+ return ConfigContainer.getConfigURL(null);
+ }
+
+ /**
+ * Returns the config file URL based on system properties. This is the
+ * same as calling <code>ConfigContainer.getConfigURL(String)</code>. The
+ * current implementation of this method parses the j3d.configURL property
+ * as a URL string. For example, the following command line would specify
+ * that the config file is taken from the file "j3dconfig" in the current
+ * directory:
+ * <ul>
+ * <code>java -Dj3d.configURL=file:j3dconfig ...</code>
+ * </ul>
+ *
+ * @param defaultURLString the default string used to construct
+ * the URL if the appropriate system properties are not defined
+ * @return the URL of the config file; null is returned if no
+ * valid URL is defined either by the system properties or the
+ * default URL string
+ */
+ public static URL getConfigURL(String defaultURLString) {
+ return ConfigContainer.getConfigURL(defaultURLString);
+ }
+
+ /**
+ * Returns all named Sensors defined by the configuration file used to
+ * create the ConfiguredUniverse, if any. Equivalent to
+ * <code>getConfigContainer().getNamedSensors()</code>.<p>
+ *
+ * With the sole exception of the Sensor assigned to the head tracker,
+ * none of the Sensors defined in the configuration file are placed into
+ * the Sensor array maintained by PhysicalEnvironment. The head tracker
+ * Sensor is the only one read by the Java 3D core and must generate reads
+ * with a full 6 degrees of freedom (3D position and 3D orientation).<p>
+ *
+ * Other Sensors need not generate reads with a full 6 degrees of freedom,
+ * although their reads must be expressed using Transform3D. Some
+ * joysticks may provide only 2D relative X and Y axis movement; dials,
+ * levers, and sliders are 1D devices, and some devices may combine dials
+ * and levers to generate 3D positional data.<p>
+ *
+ * The index names to identify left / right / dominant / non-dominant hand
+ * Sensors in the PhysicalEnvironement Sensor array are not adequate to
+ * distinguish these differences, so this method allows applications to
+ * look up Sensors based on the names bound to them in the configuration
+ * file. There are no set rules on naming. Applications that use Sensors
+ * may set up conventions for generic devices such as "mouse6D" or
+ * "joystick2D" or specific product names.<p>
+ *
+ * @return read-only Map which maps Sensor names to the associated Sensors,
+ * or null if no Sensors have been named
+ */
+ public Map getNamedSensors() {
+ if (configContainer == null)
+ return null;
+ else
+ return configContainer.getNamedSensors();
+ }
+
+ /**
+ * Returns all named ViewPlatformBehaviors defined by the configuration
+ * file used to create the ConfiguredUniverse, if any. Equivalent
+ * to <code>getConfigContainer().getNamedViewPlatformBehaviors()</code>.<p>
+ *
+ * @return read-only Map which maps behavior names to the associated
+ * ViewPlatformBehavior instances, or null if none have been named.
+ * @since Java 3D 1.3.1
+ */
+ public Map getNamedBehaviors() {
+ if (configContainer == null)
+ return null;
+ else
+ return configContainer.getNamedViewPlatformBehaviors();
+ }
+
+ /**
+ * Returns a container holding all the objects defined by the
+ * configuration file used to create the ConfiguredUniverse.
+ *
+ * @return the container
+ * @since Java 3D 1.3.1
+ */
+ public ConfigContainer getConfigContainer() {
+ return configContainer;
+ }
+
+ /**
+ * Cleanup memory references used by ConfiguredUniverse.
+ * @since Java 3D 1.3.1
+ */
+ public void cleanup() {
+ if (viewer != null) {
+ for (int i = 0 ; i < viewer.length ; i++) {
+ viewer[i].getView().removeAllCanvas3Ds();
+ viewer[i].setViewingPlatform(null);
+ viewer[i] = null;
+ }
+ }
+
+ locale = null;
+ removeAllLocales();
+ Viewer.clearViewerMap();
+
+ configContainer.clear();
+ configContainer = null;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/universe/LocaleFactory.java b/src/classes/share/com/sun/j3d/utils/universe/LocaleFactory.java
new file mode 100644
index 0000000..570d65b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/LocaleFactory.java
@@ -0,0 +1,82 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.universe;
+
+import javax.media.j3d.Locale;
+import javax.media.j3d.HiResCoord;
+import javax.media.j3d.VirtualUniverse;
+
+/**
+ * This interface defines a factory for creating Locale objects in a
+ * SimpleUniverse. Implementations of the createLocale methods in
+ * this interface should construct a new Locale object from the
+ * specified parameters. This class is used by the SimpleUniverse
+ * class to construct the default Locale used to hold the view and
+ * content branch graphs.
+ *
+ * @see Locale
+ * @see ConfiguredUniverse
+ * @see SimpleUniverse
+ *
+ * @since Java 3D 1.3
+ */
+public interface LocaleFactory {
+ /**
+ * Creates a new Locale object at the specified high resolution
+ * coordinate in the specified universe.
+ *
+ * @param universe the VirtualUniverse in which to create the Locale
+ * @param hiRes the high resolution coordinate that defines the origin
+ * of the Locale
+ */
+ public Locale createLocale(VirtualUniverse universe, HiResCoord hiRes);
+
+ /**
+ * Creates a new Locale object at (0, 0, 0) in the specified universe.
+ *
+ * @param universe the VirtualUniverse in which to create the Locale
+ */
+ public Locale createLocale(VirtualUniverse universe);
+}
diff --git a/src/classes/share/com/sun/j3d/utils/universe/MultiTransformGroup.java b/src/classes/share/com/sun/j3d/utils/universe/MultiTransformGroup.java
new file mode 100644
index 0000000..4c241a9
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/MultiTransformGroup.java
@@ -0,0 +1,140 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.universe;
+
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+/**
+ * A convenience class that effectively creates a series of TransformGroup
+ * nodes connected one to another hierarchically. For most applications,
+ * creating a MultiTransformGroup containing one transform will suffice.
+ * More sophisticated applications that use a complex portal/head tracking
+ * viewing system may find that more transforms are needed.
+ * <P>
+ * When more than one transform is needed, transform[0] is considered the
+ * "top most" transform with repsect to the scene graph, (attached to the
+ * ViewingPlatform node) and transform[numTransforms - 1] is the "bottom
+ * most" transform (the ViewPlatorm object is attached to this transform).
+ */
+public class MultiTransformGroup {
+
+ // For now just have an array of TransformGroup nodes.
+ TransformGroup[] transforms;
+
+ /**
+ * Creates a MultiTransformGroup node that contains a single transform.
+ * This is effectively equivalent to creating a single TransformGroup
+ * node.
+ */
+ public MultiTransformGroup() {
+ this(1);
+ }
+
+ /**
+ * Creates a MultiTransformGroup node that contains the specified
+ * number of transforms.
+ * <P>
+ * When more than one transform is needed, transform[0] is considered the
+ * "top most" transform with repsect to the scene graph, (attached to the
+ * ViewingPlatform node) and transform[numTransforms - 1] is the "bottom
+ * most" transform (the ViewPlatorm object is attached to this transform).
+ *
+ * @param numTransforms The number of transforms for this node to
+ * contain. If this number is less than one, one is assumed.
+ */
+ public MultiTransformGroup(int numTransforms) {
+ if (numTransforms < 1)
+ numTransforms = 1;
+
+ transforms = new TransformGroup[numTransforms];
+
+ // there is always at least one TransformGroup
+ transforms[0] = new TransformGroup();
+ transforms[0].setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+ transforms[0].setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
+ transforms[0].setCapability(TransformGroup.ALLOW_CHILDREN_WRITE);
+ transforms[0].setCapability(TransformGroup.ALLOW_CHILDREN_EXTEND);
+
+ for (int i = 1; i < numTransforms; i++) {
+ transforms[i] = new TransformGroup();
+ transforms[i].setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+ transforms[i].setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
+ transforms[i].setCapability(TransformGroup.ALLOW_CHILDREN_WRITE);
+ transforms[i].setCapability(TransformGroup.ALLOW_CHILDREN_EXTEND);
+ transforms[i-1].addChild(transforms[i]);
+ }
+ }
+
+ /**
+ * Returns the selected TransformGroup node.
+ *
+ * @param transform The index of the transform to return. The indices
+ * are in the range [0..(n - 1)] - where n was the number of transforms
+ * created. transform[0] is considered the
+ * "top most" transform with repsect to the scene graph, (attached to the
+ * ViewingPlatform node) and transform[numTransforms - 1] is the "bottom
+ * most" transform (the ViewPlatorm object is attached to this transform).
+ *
+ * @return The TransformGroup node at the designated index. If an out of
+ * range index is given, null is returned.
+ */
+ public TransformGroup getTransformGroup(int transform) {
+ if (transform >= transforms.length || transform < 0)
+ return null;
+
+ return transforms[transform];
+ }
+
+ /**
+ * Returns the number of transforms in this MultiTransformGroup object.
+ *
+ * @return The number of transforms in this object.
+ */
+ public int getNumTransforms() {
+ return transforms.length;
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/universe/PlatformGeometry.java b/src/classes/share/com/sun/j3d/utils/universe/PlatformGeometry.java
new file mode 100644
index 0000000..990197d
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/PlatformGeometry.java
@@ -0,0 +1,66 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.universe;
+
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+/**
+ * This class holds any geometry that should be associated with the
+ * ViewingPlatform object. To create a scene with a dashboard, for
+ * instance, a programmer would place the dashboard geometry under
+ * the PlatformGeometry node.
+ *
+ * @see ViewingPlatform
+ */
+public class PlatformGeometry extends BranchGroup {
+
+ /**
+ * Constructs an instance of the PlatformGeometry node.
+ */
+ public PlatformGeometry() {
+ setCapability(ALLOW_DETACH);
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/universe/SimpleUniverse.java b/src/classes/share/com/sun/j3d/utils/universe/SimpleUniverse.java
new file mode 100644
index 0000000..1b9c343
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/SimpleUniverse.java
@@ -0,0 +1,412 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.universe;
+
+import java.awt.GraphicsEnvironment;
+import java.awt.GraphicsConfiguration;
+import java.net.URL;
+
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+/**
+ * This class sets up a minimal user environment to quickly and easily
+ * get a Java 3D program up and running. This utility class creates
+ * all the necessary objects on the "view" side of the scene graph.
+ * Specifically, this class creates a locale, a single ViewingPlatform,
+ * and a Viewer object (both with their default values).
+ * Many basic Java 3D applications
+ * will find that SimpleUniverse provides all necessary functionality
+ * needed by their applications. More sophisticated applications
+ * may find that they need more control in order to get extra functionality
+ * and will not be able to use this class.
+ *
+ * @see Viewer
+ * @see ViewingPlatform
+ */
+public class SimpleUniverse extends VirtualUniverse {
+
+ /**
+ * Locale reference needed to create the "view" portion
+ * of the scene graph.
+ */
+ protected Locale locale;
+
+ /**
+ * Viewer reference needed to create the "view" portion
+ * of the scene graph.
+ */
+ protected Viewer[] viewer = null;
+
+ /**
+ * Creates a locale, a single ViewingPlatform, and
+ * and a Viewer object (both with their default values).
+ *
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ */
+ public SimpleUniverse() {
+ // call main constructor with default values.
+ this(null, 1, null, null);
+ }
+
+ /**
+ * Creates a locale, a single ViewingPlatform, and a Viewer object
+ * (with default values). The ViewingPlatform is created with the
+ * specified number of TransformGroups.
+ *
+ * @param numTransforms The number of transforms to be in the
+ * MultiTransformGroup object.
+ *
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ *
+ * @since Java 3D 1.2.1
+ */
+ public SimpleUniverse(int numTransforms) {
+ // call main constructor with default values except numTransforms
+ this(null, numTransforms, null, null);
+ }
+
+ /**
+ * Creates a locale, a single ViewingPlatform (with default values), and
+ * and a Viewer object. The Viewer object uses default values for
+ * everything but the canvas.
+ *
+ * @param canvas The canvas to associate with the Viewer object. Passing
+ * in null will cause this parameter to be ignored and a canvas to be
+ * created by the utility.
+ *
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ */
+ public SimpleUniverse(Canvas3D canvas) {
+ // call main constructor with default values for everything but
+ // the canvas parameter.
+ this(null, 1, canvas, null);
+ }
+
+ /**
+ * Creates a locale, a single ViewingPlatform, and a Viewer object
+ * The Viewer object uses default values for everything but the canvas.
+ * The ViewingPlatform is created with the specified number of
+ * TransformGroups.
+ *
+ * @param canvas The canvas to associate with the Viewer object. Passing
+ * in null will cause this parameter to be ignored and a canvas to be
+ * created by the utility.
+ * @param numTransforms The number of transforms to be in the
+ * MultiTransformGroup object.
+ *
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ * @see MultiTransformGroup
+ *
+ * @since Java 3D 1.2.1
+ */
+ public SimpleUniverse(Canvas3D canvas, int numTransforms) {
+ // call main constructor with default values except canvas
+ // and numTransforms
+ this(null, numTransforms, canvas, null);
+ }
+
+ /**
+ * Creates the "view" side of the scene graph. The passed in parameters
+ * override the default values where appropriate.
+ *
+ * @param origin The origin used to set the origin of the Locale object.
+ * If this object is null, then 0.0 is used.
+ * @param numTransforms The number of transforms to be in the
+ * MultiTransformGroup object.
+ * @param canvas The canvas to draw into. If this is null, it is
+ * ignored and a canvas will be created by the utility.
+ * @param userConfig The URL to the user's configuration file, used
+ * by the Viewer object. This is never examined and default values are
+ * always taken.
+ *
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ * @see MultiTransformGroup
+ * @deprecated use ConfiguredUniverse constructors to read a
+ * configuration file
+ */
+ public SimpleUniverse(HiResCoord origin, int numTransforms,
+ Canvas3D canvas, URL userConfig) {
+ this( origin, numTransforms, canvas, userConfig, null );
+ }
+
+ /**
+ * Creates the "view" side of the scene graph. The passed in parameters
+ * override the default values where appropriate.
+ *
+ * @param origin The origin used to set the origin of the Locale object.
+ * If this object is null, then 0.0 is used.
+ * @param numTransforms The number of transforms to be in the
+ * MultiTransformGroup object.
+ * @param canvas The canvas to draw into. If this is null, it is
+ * ignored and a canvas will be created by the utility.
+ * @param userConfig The URL to the user's configuration file, used
+ * by the Viewer object. This is never examined and default values are
+ * always taken.
+ * @param localeFactory The Locale Factory which will instantiate the
+ * locale(s) for this universe.
+ *
+ * @see Locale
+ * @see Viewer
+ * @see ViewingPlatform
+ * @see MultiTransformGroup
+ * @deprecated use ConfiguredUniverse constructors to read a
+ * configuration file
+ */
+ public SimpleUniverse(HiResCoord origin, int numTransforms,
+ Canvas3D canvas, URL userConfig, LocaleFactory localeFactory ) {
+ ViewingPlatform vwp;
+
+ createLocale( origin, localeFactory );
+
+ // Create the ViewingPlatform and Viewer objects, passing
+ // down the appropriate parameters.
+ vwp = new ViewingPlatform(numTransforms);
+ vwp.setUniverse( this );
+ viewer = new Viewer[1];
+ // viewer[0] = new Viewer(canvas, userConfig);
+ viewer[0] = new Viewer(canvas);
+ viewer[0].setViewingPlatform(vwp);
+
+ // Add the ViewingPlatform to the locale - the scene
+ // graph is now "live".
+ locale.addBranchGraph(vwp);
+ }
+
+
+ /**
+ * Creates the "view" side of the scene graph. The passed in parameters
+ * override the default values where appropriate.
+ *
+ * @param viewingPlatform The viewingPlatform to use to create
+ * the "view" side of the scene graph.
+ * @param viewer The viewer object to use to create
+ * the "view" side of the scene graph.
+ */
+ public SimpleUniverse(ViewingPlatform viewingPlatform, Viewer viewer) {
+ this( viewingPlatform, viewer, null );
+ }
+
+ /**
+ * Creates the "view" side of the scene graph. The passed in parameters
+ * override the default values where appropriate.
+ *
+ * @param viewingPlatform The viewingPlatform to use to create
+ * the "view" side of the scene graph.
+ * @param viewer The viewer object to use to create
+ * the "view" side of the scene graph.
+ * @param localeFactory The factory used to create the Locale Object
+ */
+ public SimpleUniverse(ViewingPlatform viewingPlatform, Viewer viewer,
+ LocaleFactory localeFactory ) {
+ createLocale( null, localeFactory );
+ viewingPlatform.setUniverse( this );
+
+ // Assign object references.
+ this.viewer = new Viewer[1];
+ this.viewer[0] = viewer;
+
+ // Add the ViewingPlatform to the Viewer object.
+ this.viewer[0].setViewingPlatform(viewingPlatform);
+
+ // Add the ViewingPlatform to the locale - the scene
+ // graph is now "live".
+ locale.addBranchGraph(viewingPlatform);
+ }
+
+ /**
+ * Constructor for use by Configured Universe
+ */
+ SimpleUniverse( HiResCoord origin, LocaleFactory localeFactory ) {
+ createLocale( origin, localeFactory );
+ }
+
+ /**
+ * Create the Locale using the LocaleFactory and HiRes origin,
+ * if specified.
+ */
+ private void createLocale( HiResCoord origin,
+ LocaleFactory localeFactory ) {
+
+ if (localeFactory != null) {
+ if (origin != null)
+ locale = localeFactory.createLocale(this, origin);
+ else
+ locale = localeFactory.createLocale(this);
+ }
+ else {
+ if (origin != null)
+ locale = new Locale(this, origin);
+ else
+ locale = new Locale(this);
+ }
+ }
+
+ /**
+ * Returns the Locale object associated with this scene graph.
+ *
+ * @return The Locale object used in the construction of this scene
+ * graph.
+ */
+ public Locale getLocale() {
+ return locale;
+ }
+
+ /**
+ * Returns the Viewer object associated with this scene graph.
+ * SimpleUniverse creates a single Viewer object for use in the
+ * scene graph.
+ *
+ * @return The Viewer object associated with this scene graph.
+ */
+ public Viewer getViewer() {
+ return viewer[0];
+ }
+
+ /**
+ * Returns the ViewingPlatform object associated with this scene graph.
+ *
+ * @return The ViewingPlatform object of this scene graph.
+ */
+ public ViewingPlatform getViewingPlatform() {
+ return viewer[0].getViewingPlatform();
+ }
+
+ /**
+ * Returns the Canvas3D object associated with this Java 3D Universe.
+ *
+ * @return A reference to the Canvas3D object associated with the
+ * Viewer object. This method is equivalent to calling getCanvas(0).
+ *
+ * @see Viewer
+ */
+ public Canvas3D getCanvas() {
+ return getCanvas(0);
+ }
+
+ /**
+ * Returns the Canvas3D object at the specified index associated with
+ * this Java 3D Universe.
+ *
+ * @param canvasNum The index of the Canvas3D object to retrieve.
+ * If there is no Canvas3D object for the given index, null is returned.
+ *
+ * @return A reference to the Canvas3D object associated with the
+ * Viewer object.
+ */
+ public Canvas3D getCanvas(int canvasNum) {
+ return viewer[0].getCanvas3D(canvasNum);
+ }
+
+ /**
+ * Used to add Nodes to the geometry side (as opposed to the view side)
+ * of the scene graph. This is a short cut to getting the Locale object
+ * and calling that object's addBranchGraph() method.
+ *
+ * @param bg The BranchGroup to attach to this Universe's Locale.
+ */
+ public void addBranchGraph(BranchGroup bg) {
+ locale.addBranchGraph(bg);
+ }
+
+ /**
+ * Finds the preferred <code>GraphicsConfiguration</code> object
+ * for the system. This object can then be used to create the
+ * Canvas3D objet for this system.
+ *
+ * @return The best <code>GraphicsConfiguration</code> object for
+ * the system.
+ */
+ public static GraphicsConfiguration getPreferredConfiguration() {
+ GraphicsConfigTemplate3D template = new GraphicsConfigTemplate3D();
+ String stereo;
+
+ // Check if the user has set the Java 3D stereo option.
+ // Getting the system properties causes appletviewer to fail with a
+ // security exception without a try/catch.
+
+ stereo = (String) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ return System.getProperty("j3d.stereo");
+ }
+ });
+
+ // update template based on properties.
+ if (stereo != null) {
+ if (stereo.equals("REQUIRED"))
+ template.setStereo(template.REQUIRED);
+ else if (stereo.equals("PREFERRED"))
+ template.setStereo(template.PREFERRED);
+ }
+
+ // Return the GraphicsConfiguration that best fits our needs.
+ return GraphicsEnvironment.getLocalGraphicsEnvironment().
+ getDefaultScreenDevice().getBestConfiguration(template);
+ }
+
+ /**
+ * Cleanup memory use and reference by SimpleUniverse.
+ * Typically it should be invoked by the applet's destroy method.
+ */
+ public void cleanup() {
+ viewer[0].getView().removeAllCanvas3Ds();
+ viewer[0].setViewingPlatform(null);
+ removeAllLocales();
+ // viewerMap cleanup here to prevent memory leak problem.
+ Viewer.clearViewerMap();
+
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/universe/ViewInfo.java b/src/classes/share/com/sun/j3d/utils/universe/ViewInfo.java
new file mode 100644
index 0000000..9f5c53c
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/ViewInfo.java
@@ -0,0 +1,3398 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.universe ;
+
+import java.awt.GraphicsConfiguration ;
+import java.awt.Point ;
+import java.awt.Rectangle ;
+import java.text.DecimalFormat ;
+import java.text.FieldPosition ;
+import java.util.* ;
+import javax.media.j3d.* ;
+import javax.vecmath.* ;
+
+/**
+ * Provides methods to extract synchronized transform information from a View.
+ * These transforms are derived from application scene graph information, as
+ * opposed to similar core Java 3D methods that derive transforms from
+ * internally maintained data. This allows updates to the scene graph to be
+ * synchronized with the current view platform position.<p>
+ *
+ * The architecture of the Java 3D 1.3 sample implementation introduces a
+ * frame latency between updates to the application scene graph structure and
+ * their effects on internal Java 3D state. <code>getImagePlateToVworld</code>
+ * and other methods in the core Java 3D classes use a transform from view
+ * platform coordinates to virtual world coordinates that can be out of date
+ * with respect to the state of the view platform as set by the application.
+ * When an application uses the transforms returned by those methods to update
+ * view dependent parts of the scene graph, those updates might not be
+ * synchronized with what the viewer actually sees.<p>
+ *
+ * The methods in this class work around this problem at the expense of
+ * querying the application state of the scene graph to get the current
+ * transform from view platform to virtual world coordinates. This can
+ * involve a potential performance degradation, however, since the application
+ * scene graph state is not designed for high performance queries. The view
+ * platform must also have <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability
+ * set, which potentially inhibits internal scene graph optimization.<p>
+ *
+ * On the other hand, application behaviors that create the view platform
+ * transformation directly will have access to it without the need to query it
+ * from the scene graph; in that case, the transforms from physical
+ * coordinates to view platform coordinates provided by this class are all
+ * that are needed. The <code>ALLOW_LOCAL_TO_VWORLD_READ</code> view platform
+ * capability doesn't need to be set for these applications.<p>
+ *
+ * <b>Other Synchronization Issues</b><p>
+ *
+ * Scene graph updates are guaranteed to take effect in the same frame only
+ * if run from the processStimulus() method of a Behavior. Updates from
+ * multiple behaviors are only guaranteed to take effect in the same frame if
+ * they're responding to a WakeupOnElapsedFrames(0) condition. Use a single
+ * behavior to perform view dependent updates if possible; otherwise, use
+ * WakeupOnElapsedFrames(0) and set behavior scheduling intervals to ensure
+ * that behaviors that need the current view platform transform are run after
+ * it's set. Updating scene graph elements from anything other than the
+ * Behavior thread, such as an external input thread or a renderer callback
+ * in Canvas3D, will not necessarily be synchronized with rendering.<p>
+ *
+ * Direct updates to geometry data have a different frame latency than
+ * updates to scene graph transforms and structure. In the Java 3D 1.3
+ * architecture, updates to by-reference geometry arrays and texture data have
+ * a 1-frame latency, while updates to transforms and scene graph structure
+ * have a 2-frame latency. Because of bug 4799494, which is outstanding
+ * in Java 3D 1.3.1, updates to by-copy geometry arrays also have a 1-frame
+ * latency. It is therefore recommended that view dependent scene graph
+ * updates be limited to transforms and scene graph structure only.<p>
+ *
+ * If it is not possible to avoid updating geometry directly, then these
+ * updates must be delayed by one frame in order to remain synchronized with
+ * the view platform. This can be accomplished by creating an additional
+ * behavior to actually update the geometry, separate from the behavior that
+ * computes the changes that need to be made based on current view state. If
+ * the update behavior is awakened by a behavior post from the computing
+ * behavior then the update will be delayed by a single frame.<p>
+ *
+ * <b>Implementation Notes</b><p>
+ *
+ * This utility is essentially a rewrite of a few private Java 3D core
+ * classes, but designed for public use and source code availability. The
+ * source code may be helpful in understanding some of the more complex
+ * aspects of the view model, especially with regards to various interactions
+ * between attributes which are not adequately documented. None of the actual
+ * core Java 3D source code is used, but the code is designed to comply with
+ * the view model as defined by the Java 3D Specification, so it can be
+ * considered an alternative implementation. This class will produce the
+ * same results as the Java 3D core implementation except for:<p><ul>
+ *
+ * <li>The frame latency issue for virtual world transforms.</li><p>
+ *
+ * <li>Active clip node status. If a clip node is active in the scene graph,
+ * it should override the view's back clip plane. This class has no such
+ * information, so this can't be implemented.</li><p>
+ *
+ * <li>"Infinite" view transforms for background geometry. These are simply
+ * the rotation components of the normal view transforms with adjusted
+ * clip planes. Again, this function depends upon scene graph content
+ * inaccessible to this class.</li><p>
+ *
+ * <li>Small floating point precision differences resulting from the
+ * alternative computations.</li><p>
+ *
+ * <li>Bugs in this class and the Java 3D core.</li><p>
+ *
+ * <li>Tracked head position.</li></ul><p>
+ *
+ * The last item deserves some mention. Java 3D provides no way to directly
+ * query the tracked head position being used by the renderer. The View's
+ * <code>getUserHeadToVworld</code> method always incorporates a virtual world
+ * transform that is out of date with respect to the application scene graph
+ * state. ViewInfo reads data from the head tracking sensor directly, but
+ * since head trackers are continuous input devices, getting the same data
+ * that the renderer is using is unlikely. See the source code for the
+ * private method <code>getHeadInfo</code> in this class for more information
+ * and possible workarounds.<p>
+ *
+ * <b>Thread Safety</b><p>
+ *
+ * All transforms are lazily evaluated. The <code>updateScreen</code>,
+ * <code>updateCanvas</code>, <code>updateViewPlatform</code>,
+ * <code>updateView</code>, and <code>updateHead</code> methods just set flags
+ * indicating that derived transforms need to be recomputed; they are safe to
+ * call from any thread. <code>updateCanvas</code>, for example, can safely
+ * be called from an AWT event listener.<p>
+ *
+ * Screens and view platforms can be shared between separate views in the Java
+ * 3D view model. To remain accurate, ViewInfo also allows this sharing.
+ * Since it is likely that a multi-view application has separate threads
+ * managing each view, potential concurrent modification of data associated
+ * with a screen or a view platform is internally synchronized in this class.
+ * It is safe for each thread to use its own instance of a ViewInfo
+ * corresponding to the view it is managing.<p>
+ *
+ * Otherwise, none of the other methods in this class are internally
+ * synchronized. <i>Except for the update methods mentioned above, a single
+ * instance of ViewInfo should not be used by more than one concurrent thread
+ * without external synchronization.</i><p>
+ *
+ * @since Java 3D 1.3.1
+ */
+public class ViewInfo {
+ private final static boolean verbose = false ;
+
+ /**
+ * Indicates that updates to a Screen3D associated with the View should
+ * be automatically checked with each call to a public method in this
+ * class.
+ */
+ public final static int SCREEN_AUTO_UPDATE = 1 ;
+
+ /**
+ * Indicates that updates to a Canvas3D associated with the View should
+ * be automatically checked with each call to a public method in this
+ * class.
+ */
+ public final static int CANVAS_AUTO_UPDATE = 2 ;
+
+ /**
+ * Indicates that updates to the View should be automatically checked
+ * with each call to a public method in this class.
+ */
+ public final static int VIEW_AUTO_UPDATE = 4 ;
+
+ /**
+ * Indicates that updates to the tracked head position should be
+ * automatically checked with each call to a public method in this class.
+ */
+ public final static int HEAD_AUTO_UPDATE = 8 ;
+
+ /**
+ * Indicates that updates to the ViewPlatform <code>localToVworld</code>
+ * transform should be automatically checked with each call to a public
+ * method in this class. The View must be attached to a ViewPlatform
+ * which is part of a live scene graph, and the ViewPlatform node must
+ * have its <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set.
+ */
+ public final static int PLATFORM_AUTO_UPDATE = 16 ;
+
+ //
+ // Screen3D and ViewPlatform instances are shared across multiple Views in
+ // the Java 3D view model. Since ViewInfo is per-View and we want to
+ // cache screen and platform derived data, we maintain static references
+ // to the screens and platforms here.
+ //
+ // From a design standpoint our ViewInfo objects should probably be in the
+ // scope of an object that encloses these maps so they can be gc'ed
+ // properly. This is cumbersome with the current design constraints, so
+ // for now we provide an alternative constructor to override these static
+ // maps and a method to explicitly clear them. The alternative
+ // constructor can be used to wrap this class into a multi-view context
+ // that provides the maps.
+ //
+ private static Map staticVpMap = new HashMap() ;
+ private static Map staticSiMap = new HashMap() ;
+
+ private Map screenMap = null ;
+ private Map viewPlatformMap = null ;
+
+ // The target View and some derived data.
+ private View view = null ;
+ private Sensor headTracker = null ;
+ private boolean useTracking = false ;
+ private boolean clipVirtual = false ;
+
+ // The current ViewPlatform and Canvas3D information used by this object.
+ private ViewPlatformInfo vpi = null ;
+ private int canvasCount = 0 ;
+ private Map canvasMap = new HashMap() ;
+ private CanvasInfo[] canvasInfo = new CanvasInfo[1] ;
+
+ // This View's update flags. The other update flags are maintained by
+ // ScreenInfo, CanvasInfo, and ViewPlatformInfo.
+ private boolean updateView = true ;
+ private boolean updateHead = true ;
+ private boolean autoUpdate = false ;
+ private int autoUpdateFlags = 0 ;
+
+ // Cached View policies.
+ private int viewPolicy = View.SCREEN_VIEW ;
+ private int resizePolicy = View.PHYSICAL_WORLD ;
+ private int movementPolicy = View.PHYSICAL_WORLD ;
+ private int eyePolicy = View.RELATIVE_TO_FIELD_OF_VIEW ;
+ private int projectionPolicy = View.PERSPECTIVE_PROJECTION ;
+ private int frontClipPolicy = View.PHYSICAL_EYE ;
+ private int backClipPolicy = View.PHYSICAL_EYE ;
+ private int scalePolicy = View.SCALE_SCREEN_SIZE ;
+ private boolean coeCentering = true ;
+
+ // This View's cached transforms. See ScreenInfo, CanvasInfo, and
+ // ViewPlatformInfo for the rest of the cached transforms.
+ private Transform3D coeToTrackerBase = null ;
+ private Transform3D headToHeadTracker = null ;
+
+ // These are from the head tracker read.
+ private Transform3D headTrackerToTrackerBase = null ;
+ private Transform3D trackerBaseToHeadTracker = null ;
+
+ // These are derived from the head tracker read.
+ private Transform3D headToTrackerBase = null ;
+ private Transform3D coeToHeadTracker = null ;
+
+ // Cached physical body and environment.
+ private PhysicalEnvironment env = null ;
+ private PhysicalBody body = null ;
+ private Point3d leftEyeInHead = new Point3d() ;
+ private Point3d rightEyeInHead = new Point3d() ;
+
+ // Temporary variables. These could just be new'ed as needed, but we'll
+ // assume that ViewInfo instances are used much more than they're created.
+ private Vector3d v3d = new Vector3d() ;
+ private double[] m16d = new double[16] ;
+ private Point3d leftEye = new Point3d() ;
+ private Point3d rightEye = new Point3d() ;
+ private Map newMap = new HashMap() ;
+ private Set newSet = new HashSet() ;
+
+ /**
+ * Creates a new ViewInfo for the specified View.<p>
+ *
+ * Applications are responsible for informing this class of changes to the
+ * View, its Canvas3D and Screen3D components, the tracked head position,
+ * and the ViewPlatform's <code>localToVworld</code> transform. These
+ * notifications are performed with the <code>updateView</code>,
+ * <code>updateCanvas</code>, <code>updateScreen</code>,
+ * <code>updateHead</code>, and <code>updateViewPlatform</code>
+ * methods.<p>
+ *
+ * The View must be attached to a ViewPlatform. If the ViewPlatform is
+ * attached to a live scene graph, then <code>ALLOW_POLICY_READ</code>
+ * capability must be set on the ViewPlatform node.
+ *
+ * @param view the View to use
+ * @see #updateView
+ * @see #updateCanvas updateCanvas(Canvas3D)
+ * @see #updateScreen updateScreen(Screen3D)
+ * @see #updateHead
+ * @see #updateViewPlatform
+ */
+ public ViewInfo(View view) {
+ this(view, 0) ;
+ }
+
+ /**
+ * Creates a new ViewInfo for the specified View. The View must be
+ * attached to a ViewPlatform. If the ViewPlatform is attached to a live
+ * scene graph, then <code>ALLOW_POLICY_READ</code> capability must be set
+ * on the ViewPlatform node.
+ *
+ * @param view the View to use<p>
+ * @param autoUpdateFlags a logical <code>OR</code> of any of the
+ * <code>VIEW_AUTO_UPDATE</code>, <code>CANVAS_AUTO_UPDATE</code>,
+ * <code>SCREEN_AUTO_UPDATE</code>, <code>HEAD_AUTO_UPDATE</code>, or
+ * <code>PLATFORM_AUTO_UPDATE</code> flags to control whether changes to
+ * the View, its Canvas3D or Screen3D components, the tracked head
+ * position, or the ViewPlatform's <code>localToVworld</code> transform
+ * are checked automatically with each call to a public method of this
+ * class; if a flag is not set, then the application must inform this
+ * class of updates to the corresponding data
+ */
+ public ViewInfo(View view, int autoUpdateFlags) {
+ this(view, autoUpdateFlags, staticSiMap, staticVpMap) ;
+ }
+
+ /**
+ * Creates a new ViewInfo for the specified View. The View must be
+ * attached to a ViewPlatform. If the ViewPlatform is attached to a live
+ * scene graph, then <code>ALLOW_POLICY_READ</code> capability must be set
+ * on the ViewPlatform node.<p>
+ *
+ * ViewInfo caches Screen3D and ViewPlatform data, but Screen3D and
+ * ViewPlatform instances are shared across multiple Views in the Java 3D
+ * view model. Since ViewInfo is per-View, all ViewInfo constructors
+ * except for this one use static references to manage the shared Screen3D
+ * and ViewPlatform objects. In this constructor, however, the caller
+ * supplies two Map instances to hold these references for all ViewInfo
+ * instances, so static references can be avoided; it can be used to wrap
+ * this class into a multi-view context that provides the required
+ * maps.<p>
+ *
+ * Alternatively, the other constructors can be used by calling
+ * <code>ViewInfo.clear</code> when done with ViewInfo, or by simply
+ * retaining the static references until the JVM exits.<p>
+ *
+ * @param view the View to use<p>
+ * @param autoUpdateFlags a logical <code>OR</code> of any of the
+ * <code>VIEW_AUTO_UPDATE</code>, <code>CANVAS_AUTO_UPDATE</code>,
+ * <code>SCREEN_AUTO_UPDATE</code>, <code>HEAD_AUTO_UPDATE</code>, or
+ * <code>PLATFORM_AUTO_UPDATE</code> flags to control whether changes to
+ * the View, its Canvas3D or Screen3D components, the tracked head
+ * position, or the ViewPlatform's <code>localToVworld</code> transform
+ * are checked automatically with each call to a public method of this
+ * class; if a flag is not set, then the application must inform this
+ * class of updates to the corresponding data<p>
+ * @param screenMap a writeable Map to hold Screen3D information
+ * @param viewPlatformMap a writeable Map to hold ViewPlatform information
+ */
+ public ViewInfo(View view, int autoUpdateFlags,
+ Map screenMap, Map viewPlatformMap) {
+
+ if (verbose)
+ System.err.println("ViewInfo: init " + hashCode()) ;
+ if (view == null)
+ throw new IllegalArgumentException("View is null") ;
+ if (screenMap == null)
+ throw new IllegalArgumentException("screenMap is null") ;
+ if (viewPlatformMap == null)
+ throw new IllegalArgumentException("viewPlatformMap is null") ;
+
+ this.view = view ;
+ this.screenMap = screenMap ;
+ this.viewPlatformMap = viewPlatformMap ;
+
+ if (autoUpdateFlags == 0) {
+ this.autoUpdate = false ;
+ }
+ else {
+ this.autoUpdate = true ;
+ this.autoUpdateFlags = autoUpdateFlags ;
+ }
+
+ getViewInfo() ;
+ }
+
+ /**
+ * Gets the current transforms from image plate coordinates to view
+ * platform coordinates and copies them into the given Transform3Ds.<p>
+ *
+ * With a monoscopic canvas the image plate transform is copied to the
+ * first argument and the second argument is not used. For a stereo
+ * canvas the first argument receives the left image plate transform, and
+ * if the second argument is non-null it receives the right image plate
+ * transform. These transforms are always the same unless a head mounted
+ * display driven by a single stereo canvas is in use.
+ *
+ * @param c3d the Canvas3D associated with the image plate
+ * @param ip2vpl the Transform3D to receive the left transform
+ * @param ip2vpr the Transform3D to receive the right transform, or null
+ */
+ public void getImagePlateToViewPlatform(Canvas3D c3d,
+ Transform3D ip2vpl,
+ Transform3D ip2vpr) {
+
+ CanvasInfo ci = updateCache
+ (c3d, "getImagePlateToViewPlatform", false) ;
+
+ getImagePlateToViewPlatform(ci) ;
+ ip2vpl.set(ci.plateToViewPlatform) ;
+ if (ci.useStereo && ip2vpr != null)
+ ip2vpr.set(ci.rightPlateToViewPlatform) ;
+ }
+
+ private void getImagePlateToViewPlatform(CanvasInfo ci) {
+ if (ci.updatePlateToViewPlatform) {
+ if (verbose) System.err.println("updating PlateToViewPlatform") ;
+ if (ci.plateToViewPlatform == null)
+ ci.plateToViewPlatform = new Transform3D() ;
+
+ getCoexistenceToImagePlate(ci) ;
+ getViewPlatformToCoexistence(ci) ;
+
+ ci.plateToViewPlatform.mul(ci.coeToPlate, ci.viewPlatformToCoe) ;
+ ci.plateToViewPlatform.invert() ;
+
+ if (ci.useStereo) {
+ if (ci.rightPlateToViewPlatform == null)
+ ci.rightPlateToViewPlatform = new Transform3D() ;
+
+ ci.rightPlateToViewPlatform.mul(ci.coeToRightPlate,
+ ci.viewPlatformToCoe) ;
+ ci.rightPlateToViewPlatform.invert() ;
+ }
+ ci.updatePlateToViewPlatform = false ;
+ if (verbose) t3dPrint(ci.plateToViewPlatform, "plateToVp") ;
+ }
+ }
+
+ /**
+ * Gets the current transforms from image plate coordinates to virtual
+ * world coordinates and copies them into the given Transform3Ds.<p>
+ *
+ * With a monoscopic canvas the image plate transform is copied to the
+ * first argument and the second argument is not used. For a stereo
+ * canvas the first argument receives the left image plate transform, and
+ * if the second argument is non-null it receives the right image plate
+ * transform. These transforms are always the same unless a head mounted
+ * display driven by a single stereo canvas is in use.<p>
+ *
+ * The View must be attached to a ViewPlatform which is part of a live
+ * scene graph, and the ViewPlatform node must have its
+ * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set.
+ *
+ * @param c3d the Canvas3D associated with the image plate
+ * @param ip2vwl the Transform3D to receive the left transform
+ * @param ip2vwr the Transform3D to receive the right transform, or null
+ */
+ public void getImagePlateToVworld(Canvas3D c3d,
+ Transform3D ip2vwl, Transform3D ip2vwr) {
+
+ CanvasInfo ci = updateCache(c3d, "getImagePlateToVworld", true) ;
+ getImagePlateToVworld(ci) ;
+ ip2vwl.set(ci.plateToVworld) ;
+ if (ci.useStereo && ip2vwr != null)
+ ip2vwr.set(ci.rightPlateToVworld) ;
+ }
+
+ private void getImagePlateToVworld(CanvasInfo ci) {
+ if (ci.updatePlateToVworld) {
+ if (verbose) System.err.println("updating PlateToVworld") ;
+ if (ci.plateToVworld == null)
+ ci.plateToVworld = new Transform3D() ;
+
+ getImagePlateToViewPlatform(ci) ;
+ ci.plateToVworld.mul
+ (vpi.viewPlatformToVworld, ci.plateToViewPlatform) ;
+
+ if (ci.useStereo) {
+ if (ci.rightPlateToVworld == null)
+ ci.rightPlateToVworld = new Transform3D() ;
+
+ ci.rightPlateToVworld.mul
+ (vpi.viewPlatformToVworld, ci.rightPlateToViewPlatform) ;
+ }
+ ci.updatePlateToVworld = false ;
+ }
+ }
+
+ /**
+ * Gets the current transforms from coexistence coordinates to image plate
+ * coordinates and copies them into the given Transform3Ds. The default
+ * coexistence centering enable and window movement policies are
+ * <code>true</code> and <code>PHYSICAL_WORLD</code> respectively, which
+ * will center coexistence coordinates to the middle of the canvas,
+ * aligned with the screen (image plate). A movement policy of
+ * <code>VIRTUAL_WORLD</code> centers coexistence coordinates to the
+ * middle of the screen.<p>
+ *
+ * If coexistence centering is turned off, then canvases and screens can
+ * have arbitrary positions with respect to coexistence, set through the
+ * the Screen3D <code>trackerBaseToImagePlate</code> transform and the
+ * PhysicalEnvironment <code>coexistenceToTrackerBase</code> transform.
+ * These are calibration constants used for multiple fixed screen displays.
+ * For head mounted displays the transform is determined by the user head
+ * position along with calibration parameters found in Screen3D and
+ * PhysicalBody. (See the source code for the private method
+ * <code>getEyesHMD</code> for more information).<p>
+ *
+ * With a monoscopic canvas the image plate transform is copied to the
+ * first argument and the second argument is not used. For a stereo
+ * canvas the first argument receives the left image plate transform, and
+ * if the second argument is non-null it receives the right image plate
+ * transform. These transforms are always the same unless a head mounted
+ * display driven by a single stereo canvas is in use.<p>
+ *
+ * @param c3d the Canvas3D associated with the image plate
+ * @param coe2ipl the Transform3D to receive the left transform
+ * @param coe2ipr the Transform3D to receive the right transform, or null
+ */
+ public void getCoexistenceToImagePlate(Canvas3D c3d,
+ Transform3D coe2ipl,
+ Transform3D coe2ipr) {
+
+ CanvasInfo ci = updateCache(c3d, "getCoexistenceToImagePlate", false) ;
+ getCoexistenceToImagePlate(ci) ;
+ coe2ipl.set(ci.coeToPlate) ;
+ if (ci.useStereo && coe2ipr != null)
+ coe2ipr.set(ci.coeToRightPlate) ;
+ }
+
+ private void getCoexistenceToImagePlate(CanvasInfo ci) {
+ //
+ // This method will always set coeToRightPlate even if stereo is not
+ // in use. This is necessary so that getEyeToImagePlate() can handle
+ // a monoscopic view policy of CYCLOPEAN_EYE_VIEW (which averages the
+ // left and right eye positions) when the eyepoints are expressed in
+ // coexistence coordinates or are derived from the tracked head.
+ //
+ if (ci.updateCoeToPlate) {
+ if (verbose) System.err.println("updating CoeToPlate") ;
+ if (ci.coeToPlate == null) {
+ ci.coeToPlate = new Transform3D() ;
+ ci.coeToRightPlate = new Transform3D() ;
+ }
+ if (viewPolicy == View.HMD_VIEW) {
+ // Head mounted displays have their image plates fixed with
+ // respect to the head, so get the head position in
+ // coexistence.
+ ci.coeToPlate.mul(ci.si.headTrackerToLeftPlate,
+ coeToHeadTracker) ;
+ if (ci.useStereo)
+ // This is the only case in the view model in which the
+ // right plate transform could be different from the left.
+ ci.coeToRightPlate.mul(ci.si.headTrackerToRightPlate,
+ coeToHeadTracker) ;
+ else
+ ci.coeToRightPlate.set(ci.coeToPlate) ;
+ }
+ else if (coeCentering) {
+ // The default, for fixed single screen displays with no
+ // motion tracking. The transform is just a translation.
+ if (movementPolicy == View.PHYSICAL_WORLD)
+ // The default. Coexistence is centered in the window.
+ v3d.set(ci.canvasX + (ci.canvasWidth / 2.0),
+ ci.canvasY + (ci.canvasHeight / 2.0), 0.0) ;
+ else
+ // Coexistence is centered in the screen.
+ v3d.set(ci.si.screenWidth / 2.0,
+ ci.si.screenHeight / 2.0, 0.0) ;
+
+ ci.coeToPlate.set(v3d) ;
+ ci.coeToRightPlate.set(v3d) ;
+ }
+ else {
+ // Coexistence centering should be false for multiple fixed
+ // screens and/or motion tracking. trackerBaseToImagePlate
+ // and coexistenceToTrackerBase are used explicitly.
+ ci.coeToPlate.mul(ci.si.trackerBaseToPlate, coeToTrackerBase) ;
+ ci.coeToRightPlate.set(ci.coeToPlate) ;
+ }
+ ci.updateCoeToPlate = false ;
+ if (verbose) t3dPrint(ci.coeToPlate, "coeToPlate") ;
+ }
+ }
+
+ /**
+ * Gets the current transform from view platform coordinates to
+ * coexistence coordinates and copies it into the given transform. View
+ * platform coordinates are always aligned with coexistence coordinates
+ * but may differ in scale and in Y and Z offset. The scale is derived
+ * from the window resize and screen scale policies, while the offset is
+ * derived from the view attach policy.<p>
+ *
+ * Java 3D constructs a view from the physical position of the eyes
+ * relative to the physical positions of the image plates; it then uses a
+ * view platform to position that physical configuration into the virtual
+ * world and from there computes the correct projections of the virtual
+ * world onto the physical image plates. Coexistence coordinates are used
+ * to place the physical positions of the view platform, eyes, head, image
+ * plate, sensors, and tracker base in relation to each other. The view
+ * platform is positioned with respect to the virtual world through the
+ * scene graph, so the view platform to coexistence transform defines the
+ * space in which the virtual world and physical world coexist.<p>
+ *
+ * This method requires a Canvas3D. A different transform may be returned
+ * for each canvas in the view if any of the following apply:<p><ul>
+ *
+ * <li>The window resize policy is <code>PHYSICAL_WORLD</code>, which
+ * alters the scale depending upon the width of the canvas.</li><p>
+ *
+ * <li>The screen scale policy is <code>SCALE_SCREEN_SIZE</code>,
+ * which alters the scale depending upon the width of the screen
+ * associated with the canvas.</li><p>
+ *
+ * <li>A window eyepoint policy of <code>RELATIVE_TO_FIELD_OF_VIEW</code>
+ * with a view attach policy of <code>NOMINAL_HEAD</code> in effect,
+ * which sets the view platform Z offset in coexistence coordinates
+ * based on the width of the canvas. These are the default policies.
+ * The offset also follows the width of the canvas when the
+ * <code>NOMINAL_FEET</code> view attach policy is used.</li></ul>
+ *
+ * @param c3d the Canvas3D to use
+ * @param vp2coe the Transform3D to receive the transform
+ */
+ public void getViewPlatformToCoexistence(Canvas3D c3d,
+ Transform3D vp2coe) {
+
+ CanvasInfo ci = updateCache
+ (c3d, "getViewPlatformToCoexistence", false) ;
+
+ getViewPlatformToCoexistence(ci) ;
+ vp2coe.set(ci.viewPlatformToCoe) ;
+ }
+
+ private void getViewPlatformToCoexistence(CanvasInfo ci) {
+ if (!ci.updateViewPlatformToCoe) return ;
+ if (verbose) System.err.println("updating ViewPlatformToCoe") ;
+ if (ci.viewPlatformToCoe == null)
+ ci.viewPlatformToCoe = new Transform3D() ;
+ //
+ // The scale from view platform coordinates to coexistence coordinates
+ // has two components -- the screen scale and the window scale. The
+ // window scale only applies if the resize policy is PHYSICAL_WORLD.
+ //
+ // This scale is not the same as the vworld to view platform scale.
+ // The latter is contained in the view platform's localToVworld
+ // transform as defined by the scene graph. The complete scale factor
+ // from virtual units to physical units is the product of the vworld
+ // to view platform scale and the view platform to coexistence scale.
+ //
+ getScreenScale(ci) ;
+ if (resizePolicy == View.PHYSICAL_WORLD)
+ ci.viewPlatformToCoe.setScale(ci.screenScale * ci.windowScale) ;
+ else
+ ci.viewPlatformToCoe.setScale(ci.screenScale) ;
+
+ if (viewPolicy == View.HMD_VIEW) {
+ // In HMD mode view platform coordinates are the same as
+ // coexistence coordinates, except for scale.
+ ci.updateViewPlatformToCoe = false ;
+ return ;
+ }
+
+ //
+ // Otherwise, get the offset of the origin of view platform
+ // coordinates relative to the origin of coexistence. This is is
+ // specified by two policies: the view platform's view attach policy
+ // and the physical environment's coexistence center in pworld policy.
+ //
+ double eyeOffset ;
+ double eyeHeight = body.getNominalEyeHeightFromGround() ;
+ int viewAttachPolicy = view.getViewPlatform().getViewAttachPolicy() ;
+ int pworldAttachPolicy = env.getCoexistenceCenterInPworldPolicy() ;
+
+ if (eyePolicy == View.RELATIVE_TO_FIELD_OF_VIEW)
+ // The view platform origin is the same as the eye position.
+ eyeOffset = ci.getFieldOfViewOffset() ;
+ else
+ // The view platform origin is independent of the eye position.
+ eyeOffset = body.getNominalEyeOffsetFromNominalScreen() ;
+
+ if (pworldAttachPolicy == View.NOMINAL_SCREEN) {
+ // The default. The physical coexistence origin locates the
+ // nominal screen. This is rarely, if ever, set to anything
+ // else, and the intended effects of the other settings are
+ // not well documented.
+ if (viewAttachPolicy == View.NOMINAL_HEAD) {
+ // The default. The view platform origin is at the origin
+ // of the nominal head in coexistence coordinates, offset
+ // from the screen along +Z. If the window eyepoint
+ // policy is RELATIVE_TO_FIELD_OF_VIEW, then the eyepoint
+ // is the same as the view platform origin.
+ v3d.set(0.0, 0.0, eyeOffset) ;
+ }
+ else if (viewAttachPolicy == View.NOMINAL_SCREEN) {
+ // View platform and coexistence are the same except for
+ // scale.
+ v3d.set(0.0, 0.0, 0.0) ;
+ }
+ else {
+ // The view platform origin is at the ground beneath the
+ // head.
+ v3d.set(0.0, -eyeHeight, eyeOffset) ;
+ }
+ }
+ else if (pworldAttachPolicy == View.NOMINAL_HEAD) {
+ // The physical coexistence origin locates the nominal head.
+ if (viewAttachPolicy == View.NOMINAL_HEAD) {
+ // The view platform origin is set to the head;
+ // coexistence and view platform coordinates differ only
+ // in scale.
+ v3d.set(0.0, 0.0, 0.0) ;
+ }
+ else if (viewAttachPolicy == View.NOMINAL_SCREEN) {
+ // The view platform is set in front of the head, at the
+ // nominal screen location.
+ v3d.set(0.0, 0.0, -eyeOffset) ;
+ }
+ else {
+ // The view platform origin is at the ground beneath the
+ // head.
+ v3d.set(0.0, -eyeHeight, 0.0) ;
+ }
+ }
+ else {
+ // The physical coexistence origin locates the nominal feet.
+ if (viewAttachPolicy == View.NOMINAL_HEAD) {
+ v3d.set(0.0, eyeHeight, 0.0) ;
+ }
+ else if (viewAttachPolicy == View.NOMINAL_SCREEN) {
+ v3d.set(0.0, eyeHeight, -eyeOffset) ;
+ }
+ else {
+ v3d.set(0.0, 0.0, 0.0) ;
+ }
+ }
+
+ ci.viewPlatformToCoe.setTranslation(v3d) ;
+ ci.updateViewPlatformToCoe = false ;
+ if (verbose) t3dPrint(ci.viewPlatformToCoe, "vpToCoe") ;
+ }
+
+ /**
+ * Gets the current transform from coexistence coordinates to
+ * view platform coordinates and copies it into the given transform.<p>
+ *
+ * This method requires a Canvas3D. The returned transform may differ
+ * across canvases for the same reasons as discussed in the description of
+ * <code>getViewPlatformToCoexistence</code>.<p>
+ *
+ * @param c3d the Canvas3D to use
+ * @param coe2vp the Transform3D to receive the transform
+ * @see #getViewPlatformToCoexistence
+ * getViewPlatformToCoexistence(Canvas3D, Transform3D)
+ */
+ public void getCoexistenceToViewPlatform(Canvas3D c3d,
+ Transform3D coe2vp) {
+
+ CanvasInfo ci = updateCache
+ (c3d, "getCoexistenceToViewPlatform", false) ;
+
+ getCoexistenceToViewPlatform(ci) ;
+ coe2vp.set(ci.coeToViewPlatform) ;
+ }
+
+ private void getCoexistenceToViewPlatform(CanvasInfo ci) {
+ if (ci.updateCoeToViewPlatform) {
+ if (verbose) System.err.println("updating CoeToViewPlatform") ;
+ if (ci.coeToViewPlatform == null)
+ ci.coeToViewPlatform = new Transform3D() ;
+
+ getViewPlatformToCoexistence(ci) ;
+ ci.coeToViewPlatform.invert(ci.viewPlatformToCoe) ;
+
+ ci.updateCoeToViewPlatform = false ;
+ if (verbose) t3dPrint(ci.coeToViewPlatform, "coeToVp") ;
+ }
+ }
+
+ /**
+ * Gets the current transform from coexistence coordinates to virtual
+ * world coordinates and copies it into the given transform.<p>
+ *
+ * The View must be attached to a ViewPlatform which is part of a live
+ * scene graph, and the ViewPlatform node must have its
+ * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set.<p>
+ *
+ * This method requires a Canvas3D. The returned transform may differ
+ * across canvases for the same reasons as discussed in the description of
+ * <code>getViewPlatformToCoexistence</code>.<p>
+ *
+ * @param c3d the Canvas3D to use
+ * @param coe2vw the Transform3D to receive the transform
+ * @see #getViewPlatformToCoexistence
+ * getViewPlatformToCoexistence(Canvas3D, Transform3D)
+ */
+ public void getCoexistenceToVworld(Canvas3D c3d,
+ Transform3D coe2vw) {
+
+ CanvasInfo ci = updateCache(c3d, "getCoexistenceToVworld", true) ;
+ getCoexistenceToVworld(ci) ;
+ coe2vw.set(ci.coeToVworld) ;
+ }
+
+ private void getCoexistenceToVworld(CanvasInfo ci) {
+ if (ci.updateCoeToVworld) {
+ if (verbose) System.err.println("updating CoexistenceToVworld") ;
+ if (ci.coeToVworld == null) ci.coeToVworld = new Transform3D() ;
+
+ getCoexistenceToViewPlatform(ci) ;
+ ci.coeToVworld.mul(vpi.viewPlatformToVworld,
+ ci.coeToViewPlatform) ;
+
+ ci.updateCoeToVworld = false ;
+ }
+ }
+
+ /**
+ * Gets the transforms from eye coordinates to image plate coordinates and
+ * copies them into the Transform3Ds specified.<p>
+ *
+ * When head tracking is used the eye positions are taken from the head
+ * position and set in relation to the image plates with each Screen3D's
+ * <code>trackerBaseToImagePlate</code> transform. Otherwise the window
+ * eyepoint policy is used to derive the eyepoint relative to the image
+ * plate. When using a head mounted display the eye position is
+ * determined solely by calibration constants in Screen3D and
+ * PhysicalBody; see the source code for the private method
+ * <code>getEyesHMD</code> for more information.<p>
+ *
+ * Eye coordinates are always aligned with image plate coordinates, so
+ * these transforms are always just translations. With a monoscopic
+ * canvas the eye transform is copied to the first argument and the second
+ * argument is not used. For a stereo canvas the first argument receives
+ * the left eye transform, and if the second argument is non-null it
+ * receives the right eye transform.
+ *
+ * @param c3d the Canvas3D associated with the image plate
+ * @param e2ipl the Transform3D to receive left transform
+ * @param e2ipr the Transform3D to receive right transform, or null
+ */
+ public void getEyeToImagePlate(Canvas3D c3d,
+ Transform3D e2ipl, Transform3D e2ipr) {
+
+ CanvasInfo ci = updateCache(c3d, "getEyeToImagePlate", false) ;
+ getEyeToImagePlate(ci) ;
+ e2ipl.set(ci.eyeToPlate) ;
+ if (ci.useStereo && e2ipr != null)
+ e2ipr.set(ci.rightEyeToPlate) ;
+ }
+
+ private void getEyeToImagePlate(CanvasInfo ci) {
+ if (ci.updateEyeInPlate) {
+ if (verbose) System.err.println("updating EyeInPlate") ;
+ if (ci.eyeToPlate == null)
+ ci.eyeToPlate = new Transform3D() ;
+
+ if (viewPolicy == View.HMD_VIEW) {
+ getEyesHMD(ci) ;
+ }
+ else if (useTracking) {
+ getEyesTracked(ci) ;
+ }
+ else {
+ getEyesFixedScreen(ci) ;
+ }
+ ci.updateEyeInPlate = false ;
+ if (verbose) System.err.println("eyeInPlate: " + ci.eyeInPlate) ;
+ }
+ }
+
+ //
+ // Get physical eye positions for head mounted displays. These are
+ // determined solely by the headTrackerToImagePlate and headToHeadTracker
+ // calibration constants defined by Screen3D and the PhysicalBody.
+ //
+ // Note that headTrackerLeftToImagePlate and headTrackerToRightImagePlate
+ // should be set according to the *apparent* position and orientation of
+ // the image plates, relative to the head and head tracker, as viewed
+ // through the HMD optics. This is also true of the "physical" screen
+ // width and height specified by the Screen3D -- they should be the
+ // *apparent* width and height as viewed through the HMD optics. They
+ // must be set directly through the Screen3D methods; the default pixel
+ // metrics of 90 pixels/inch used by Java 3D aren't appropriate for HMD
+ // optics.
+ //
+ // Most HMDs have 100% overlap between the left and right displays; in
+ // that case, headTrackerToLeftImagePlate and headTrackerToRightImagePlate
+ // should be identical. The HMD manufacturer's specifications of the
+ // optics in terms of field of view, image overlap, and distance to the
+ // focal plane should be used to derive these parameters.
+ //
+ private void getEyesHMD(CanvasInfo ci) {
+ if (ci.useStereo) {
+ // This case is for head mounted displays driven by a single
+ // stereo canvas on a single screen. These use a field sequential
+ // stereo signal to split the left and right images.
+ leftEye.set(leftEyeInHead) ;
+ headToHeadTracker.transform(leftEye) ;
+ ci.si.headTrackerToLeftPlate.transform(leftEye,
+ ci.eyeInPlate) ;
+ rightEye.set(rightEyeInHead) ;
+ headToHeadTracker.transform(rightEye) ;
+ ci.si.headTrackerToRightPlate.transform(rightEye,
+ ci.rightEyeInPlate) ;
+ if (ci.rightEyeToPlate == null)
+ ci.rightEyeToPlate = new Transform3D() ;
+
+ v3d.set(ci.rightEyeInPlate) ;
+ ci.rightEyeToPlate.set(v3d) ;
+ }
+ else {
+ // This case is for 2-channel head mounted displays driven by two
+ // monoscopic screens, one for each eye.
+ switch (ci.monoscopicPolicy) {
+ case View.LEFT_EYE_VIEW:
+ leftEye.set(leftEyeInHead) ;
+ headToHeadTracker.transform(leftEye) ;
+ ci.si.headTrackerToLeftPlate.transform(leftEye,
+ ci.eyeInPlate) ;
+ break ;
+ case View.RIGHT_EYE_VIEW:
+ rightEye.set(rightEyeInHead) ;
+ headToHeadTracker.transform(rightEye) ;
+ ci.si.headTrackerToRightPlate.transform(rightEye,
+ ci.eyeInPlate) ;
+ break ;
+ case View.CYCLOPEAN_EYE_VIEW:
+ default:
+ throw new IllegalStateException
+ ("Illegal monoscopic view policy for 2-channel HMD") ;
+ }
+ }
+ v3d.set(ci.eyeInPlate) ;
+ ci.eyeToPlate.set(v3d) ;
+ }
+
+ private void getEyesTracked(CanvasInfo ci) {
+ leftEye.set(leftEyeInHead) ;
+ rightEye.set(rightEyeInHead) ;
+ headToTrackerBase.transform(leftEye) ;
+ headToTrackerBase.transform(rightEye) ;
+ if (coeCentering) {
+ // Coexistence and tracker base coordinates are the same.
+ // Centering is normally turned off for tracking.
+ getCoexistenceToImagePlate(ci) ;
+ ci.coeToPlate.transform(leftEye) ;
+ ci.coeToRightPlate.transform(rightEye) ;
+ }
+ else {
+ // The normal policy for head tracking.
+ ci.si.trackerBaseToPlate.transform(leftEye) ;
+ ci.si.trackerBaseToPlate.transform(rightEye) ;
+ }
+ setEyeScreenRelative(ci, leftEye, rightEye) ;
+ }
+
+ private void getEyesFixedScreen(CanvasInfo ci) {
+ switch (eyePolicy) {
+ case View.RELATIVE_TO_FIELD_OF_VIEW:
+ double z = ci.getFieldOfViewOffset() ;
+ setEyeWindowRelative(ci, z, z) ;
+ break ;
+ case View.RELATIVE_TO_WINDOW:
+ setEyeWindowRelative(ci,
+ ci.leftManualEyeInPlate.z,
+ ci.rightManualEyeInPlate.z) ;
+ break ;
+ case View.RELATIVE_TO_SCREEN:
+ setEyeScreenRelative(ci,
+ ci.leftManualEyeInPlate,
+ ci.rightManualEyeInPlate) ;
+ break ;
+ case View.RELATIVE_TO_COEXISTENCE:
+ view.getLeftManualEyeInCoexistence(leftEye) ;
+ view.getRightManualEyeInCoexistence(rightEye) ;
+
+ getCoexistenceToImagePlate(ci) ;
+ ci.coeToPlate.transform(leftEye) ;
+ ci.coeToRightPlate.transform(rightEye) ;
+ setEyeScreenRelative(ci, leftEye, rightEye) ;
+ break ;
+ }
+ }
+
+ private void setEyeWindowRelative(CanvasInfo ci,
+ double leftZ, double rightZ) {
+
+ // Eye position X is offset from the window center.
+ double centerX = (ci.canvasX + (ci.canvasWidth / 2.0)) ;
+ leftEye.x = centerX + leftEyeInHead.x ;
+ rightEye.x = centerX + rightEyeInHead.x ;
+
+ // Eye position Y is always the canvas center.
+ leftEye.y = rightEye.y = ci.canvasY + (ci.canvasHeight / 2.0) ;
+
+ // Eye positions Z are as given.
+ leftEye.z = leftZ ;
+ rightEye.z = rightZ ;
+
+ setEyeScreenRelative(ci, leftEye, rightEye) ;
+ }
+
+ private void setEyeScreenRelative(CanvasInfo ci,
+ Point3d leftEye, Point3d rightEye) {
+ if (ci.useStereo) {
+ ci.eyeInPlate.set(leftEye) ;
+ ci.rightEyeInPlate.set(rightEye) ;
+
+ if (ci.rightEyeToPlate == null)
+ ci.rightEyeToPlate = new Transform3D() ;
+
+ v3d.set(ci.rightEyeInPlate) ;
+ ci.rightEyeToPlate.set(v3d) ;
+ }
+ else {
+ switch (ci.monoscopicPolicy) {
+ case View.CYCLOPEAN_EYE_VIEW:
+ ci.eyeInPlate.set((leftEye.x + rightEye.x) / 2.0,
+ (leftEye.y + rightEye.y) / 2.0,
+ (leftEye.z + rightEye.z) / 2.0) ;
+ break ;
+ case View.LEFT_EYE_VIEW:
+ ci.eyeInPlate.set(leftEye) ;
+ break ;
+ case View.RIGHT_EYE_VIEW:
+ ci.eyeInPlate.set(rightEye) ;
+ break ;
+ }
+ }
+ v3d.set(ci.eyeInPlate) ;
+ ci.eyeToPlate.set(v3d) ;
+ }
+
+ /**
+ * Gets the current transforms from eye coordinates to view platform
+ * coordinates and copies them into the given Transform3Ds.<p>
+ *
+ * With a monoscopic canvas the eye transform is copied to the first
+ * argument and the second argument is not used. For a stereo canvas the
+ * first argument receives the left eye transform, and if the second
+ * argument is non-null it receives the right eye transform.<p>
+ *
+ * This method requires a Canvas3D. When using a head mounted display,
+ * head tracking with fixed screens, or a window eyepoint policy of
+ * <code>RELATIVE_TO_COEXISTENCE</code>, then the transforms returned may
+ * be different for each canvas if stereo is not in use and they have
+ * different monoscopic view policies. They may additionally differ in
+ * scale across canvases with the <code>PHYSICAL_WORLD</code> window
+ * resize policy or the <code>SCALE_SCREEN_SIZE</code> screen scale
+ * policy, which alter the scale depending upon the width of the canvas or
+ * the width of the screen respectively.<p>
+ *
+ * With window eyepoint policies of <code>RELATIVE_TO_FIELD_OF_VIEW</code>,
+ * <code>RELATIVE_TO_SCREEN</code>, or <code>RELATIVE_TO_WINDOW</code>,
+ * then the transforms returned may differ across canvases due to
+ * the following additional conditions:<p><ul>
+ *
+ * <li>The window eyepoint policy is <code>RELATIVE_TO_WINDOW</code> or
+ * <code>RELATIVE_TO_SCREEN</code>, in which case the manual eye
+ * position in image plate can be set differently for each
+ * canvas.</li><p>
+ *
+ * <li>The window eyepoint policy is <code>RELATIVE_TO_FIELD_OF_VIEW</code>
+ * and the view attach policy is <code>NOMINAL_SCREEN</code>, which
+ * decouples the view platform's canvas Z offset from the eyepoint's
+ * canvas Z offset.</li><p>
+ *
+ * <li>The eyepoint X and Y coordinates are centered in the canvas with a
+ * window eyepoint policy of <code>RELATIVE_TO_FIELD_OF_VIEW</code>
+ * or <code>RELATIVE_TO_WINDOW</code>, and a window movement policy
+ * of <code>VIRTUAL_WORLD</code> centers the view platform's X and Y
+ * coordinates to the middle of the screen.</li><p>
+ *
+ * <li>Coexistence centering is set false, which allows each canvas and
+ * screen to have a different position with respect to coexistence
+ * coordinates.</li></ul>
+ *
+ * @param c3d the Canvas3D to use
+ * @param e2vpl the Transform3D to receive the left transform
+ * @param e2vpr the Transform3D to receive the right transform, or null
+ */
+ public void getEyeToViewPlatform(Canvas3D c3d,
+ Transform3D e2vpl, Transform3D e2vpr) {
+
+ CanvasInfo ci = updateCache(c3d, "getEyeToViewPlatform", false) ;
+ getEyeToViewPlatform(ci) ;
+ e2vpl.set(ci.eyeToViewPlatform) ;
+ if (ci.useStereo && e2vpr != null)
+ e2vpr.set(ci.rightEyeToViewPlatform) ;
+ }
+
+ private void getEyeToViewPlatform(CanvasInfo ci) {
+ if (ci.updateEyeToViewPlatform) {
+ if (verbose) System.err.println("updating EyeToViewPlatform") ;
+ if (ci.eyeToViewPlatform == null)
+ ci.eyeToViewPlatform = new Transform3D() ;
+
+ getEyeToImagePlate(ci) ;
+ getImagePlateToViewPlatform(ci) ;
+ ci.eyeToViewPlatform.mul(ci.plateToViewPlatform, ci.eyeToPlate) ;
+
+ if (ci.useStereo) {
+ if (ci.rightEyeToViewPlatform == null)
+ ci.rightEyeToViewPlatform = new Transform3D() ;
+
+ ci.rightEyeToViewPlatform.mul
+ (ci.rightPlateToViewPlatform, ci.rightEyeToPlate) ;
+ }
+ ci.updateEyeToViewPlatform = false ;
+ if (verbose) t3dPrint(ci.eyeToViewPlatform, "eyeToVp") ;
+ }
+ }
+
+ /**
+ * Gets the current transforms from view platform coordinates to eye
+ * coordinates and copies them into the given Transform3Ds.<p>
+ *
+ * With a monoscopic canvas the eye transform is copied to the first
+ * argument and the second argument is not used. For a stereo canvas the
+ * first argument receives the left eye transform, and if the second
+ * argument is non-null it receives the right eye transform.<p>
+ *
+ * This method requires a Canvas3D. The transforms returned may differ
+ * across canvases for all the same reasons discussed in the description
+ * of <code>getEyeToViewPlatform</code>.
+ *
+ * @param c3d the Canvas3D to use
+ * @param vp2el the Transform3D to receive the left transform
+ * @param vp2er the Transform3D to receive the right transform, or null
+ * @see #getEyeToViewPlatform
+ * getEyeToViewPlatform(Canvas3D, Transform3D, Transform3D)
+ */
+ public void getViewPlatformToEye(Canvas3D c3d,
+ Transform3D vp2el, Transform3D vp2er) {
+
+ CanvasInfo ci = updateCache(c3d, "getViewPlatformToEye", false) ;
+ getViewPlatformToEye(ci) ;
+ vp2el.set(ci.viewPlatformToEye) ;
+ if (ci.useStereo && vp2er != null)
+ vp2er.set(ci.viewPlatformToRightEye) ;
+ }
+
+ private void getViewPlatformToEye(CanvasInfo ci) {
+ if (ci.updateViewPlatformToEye) {
+ if (verbose) System.err.println("updating ViewPlatformToEye") ;
+ if (ci.viewPlatformToEye == null)
+ ci.viewPlatformToEye = new Transform3D() ;
+
+ getEyeToViewPlatform(ci) ;
+ ci.viewPlatformToEye.invert(ci.eyeToViewPlatform) ;
+
+ if (ci.useStereo) {
+ if (ci.viewPlatformToRightEye == null)
+ ci.viewPlatformToRightEye = new Transform3D() ;
+
+ ci.viewPlatformToRightEye.invert(ci.rightEyeToViewPlatform) ;
+ }
+ ci.updateViewPlatformToEye = false ;
+ }
+ }
+
+ /**
+ * Gets the current transforms from eye coordinates to virtual world
+ * coordinates and copies them into the given Transform3Ds.<p>
+ *
+ * With a monoscopic canvas the eye transform is copied to the first
+ * argument and the second argument is not used. For a stereo canvas the
+ * first argument receives the left eye transform, and if the second
+ * argument is non-null it receives the right eye transform.<p>
+ *
+ * The View must be attached to a ViewPlatform which is part of a live
+ * scene graph, and the ViewPlatform node must have its
+ * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set.<p>
+ *
+ * This method requires a Canvas3D. The transforms returned may differ
+ * across canvases for all the same reasons discussed in the description
+ * of <code>getEyeToViewPlatform</code>.
+ *
+ * @param c3d the Canvas3D to use
+ * @param e2vwl the Transform3D to receive the left transform
+ * @param e2vwr the Transform3D to receive the right transform, or null
+ * @see #getEyeToViewPlatform
+ * getEyeToViewPlatform(Canvas3D, Transform3D, Transform3D)
+ */
+ public void getEyeToVworld(Canvas3D c3d,
+ Transform3D e2vwl, Transform3D e2vwr) {
+
+ CanvasInfo ci = updateCache(c3d, "getEyeToVworld", true) ;
+ getEyeToVworld(ci) ;
+ e2vwl.set(ci.eyeToVworld) ;
+ if (ci.useStereo && e2vwr != null)
+ e2vwr.set(ci.rightEyeToVworld) ;
+ }
+
+ private void getEyeToVworld(CanvasInfo ci) {
+ if (ci.updateEyeToVworld) {
+ if (verbose) System.err.println("updating EyeToVworld") ;
+ if (ci.eyeToVworld == null)
+ ci.eyeToVworld = new Transform3D() ;
+
+ getEyeToViewPlatform(ci) ;
+ ci.eyeToVworld.mul
+ (vpi.viewPlatformToVworld, ci.eyeToViewPlatform) ;
+
+ if (ci.useStereo) {
+ if (ci.rightEyeToVworld == null)
+ ci.rightEyeToVworld = new Transform3D() ;
+
+ ci.rightEyeToVworld.mul
+ (vpi.viewPlatformToVworld, ci.rightEyeToViewPlatform) ;
+ }
+ ci.updateEyeToVworld = false ;
+ }
+ }
+
+ /**
+ * Gets the transforms from eye coordinates to clipping coordinates
+ * and copies them into the given Transform3Ds. These transforms take
+ * a viewing volume bounded by the physical canvas edges and the
+ * physical front and back clip planes and project it into a range
+ * bound to [-1.0 .. +1.0] on each of the X, Y, and Z axes. If a
+ * perspective projection has been specified then the physical image
+ * plate eye location defines the apex of a viewing frustum;
+ * otherwise, the orientation of the image plate determines the
+ * direction of a parallel projection.<p>
+ *
+ * With a monoscopic canvas the projection transform is copied to the
+ * first argument and the second argument is not used. For a stereo
+ * canvas the first argument receives the left projection transform,
+ * and if the second argument is non-null it receives the right
+ * projection transform.<p>
+ *
+ * If either of the clip policies <code>VIRTUAL_EYE</code> or
+ * <code>VIRTUAL_SCREEN</code> are used, then the View should be attached
+ * to a ViewPlatform that is part of a live scene graph and that has its
+ * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set; otherwise, a
+ * scale factor of 1.0 will be used for the scale factor from virtual
+ * world units to view platform units.
+ *
+ * @param c3d the Canvas3D to use
+ * @param e2ccl the Transform3D to receive left transform
+ * @param e2ccr the Transform3D to receive right transform, or null
+ */
+ public void getProjection(Canvas3D c3d,
+ Transform3D e2ccl, Transform3D e2ccr) {
+
+ CanvasInfo ci = updateCache(c3d, "getProjection", true) ;
+ getProjection(ci) ;
+ e2ccl.set(ci.projection) ;
+ if (ci.useStereo && e2ccr != null)
+ e2ccr.set(ci.rightProjection) ;
+ }
+
+ private void getProjection(CanvasInfo ci) {
+ if (ci.updateProjection) {
+ if (verbose) System.err.println("updating Projection") ;
+ if (ci.projection == null)
+ ci.projection = new Transform3D() ;
+
+ getEyeToImagePlate(ci) ;
+ getClipDistances(ci) ;
+
+ // Note: core Java 3D code insists that the back clip plane
+ // relative to the image plate must be the same left back clip
+ // distance for both the left and right eye. Not sure why this
+ // should be, but the same is done here for compatibility.
+ double backClip = getBackClip(ci, ci.eyeInPlate) ;
+ computeProjection(ci, ci.eyeInPlate,
+ getFrontClip(ci, ci.eyeInPlate),
+ backClip, ci.projection) ;
+
+ if (ci.useStereo) {
+ if (ci.rightProjection == null)
+ ci.rightProjection = new Transform3D() ;
+
+ computeProjection(ci, ci.rightEyeInPlate,
+ getFrontClip(ci, ci.rightEyeInPlate),
+ backClip, ci.rightProjection) ;
+ }
+ ci.updateProjection = false ;
+ if (verbose) t3dPrint(ci.projection, "projection") ;
+ }
+ }
+
+ /**
+ * Gets the transforms from clipping coordinates to eye coordinates
+ * and copies them into the given Transform3Ds. These transforms take
+ * the clip space volume bounded by the range [-1.0 .. + 1.0] on each
+ * of the X, Y, and Z and project it into eye coordinates.<p>
+ *
+ * With a monoscopic canvas the projection transform is copied to the
+ * first argument and the second argument is not used. For a stereo
+ * canvas the first argument receives the left projection transform, and
+ * if the second argument is non-null it receives the right projection
+ * transform.<p>
+ *
+ * If either of the clip policies <code>VIRTUAL_EYE</code> or
+ * <code>VIRTUAL_SCREEN</code> are used, then the View should be attached
+ * to a ViewPlatform that is part of a live scene graph and that has its
+ * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set; otherwise, a
+ * scale factor of 1.0 will be used for the scale factor from virtual
+ * world units to view platform units.
+ *
+ * @param c3d the Canvas3D to use
+ * @param cc2el the Transform3D to receive left transform
+ * @param cc2er the Transform3D to receive right transform, or null
+ */
+ public void getInverseProjection(Canvas3D c3d,
+ Transform3D cc2el, Transform3D cc2er) {
+
+ CanvasInfo ci = updateCache(c3d, "getInverseProjection", true) ;
+ getInverseProjection(ci) ;
+ cc2el.set(ci.inverseProjection) ;
+ if (ci.useStereo && cc2er != null)
+ cc2er.set(ci.inverseRightProjection) ;
+ }
+
+ private void getInverseProjection(CanvasInfo ci) {
+ if (ci.updateInverseProjection) {
+ if (verbose) System.err.println("updating InverseProjection") ;
+ if (ci.inverseProjection == null)
+ ci.inverseProjection = new Transform3D() ;
+
+ getProjection(ci) ;
+ ci.inverseProjection.invert(ci.projection) ;
+
+ if (ci.useStereo) {
+ if (ci.inverseRightProjection == null)
+ ci.inverseRightProjection = new Transform3D() ;
+
+ ci.inverseRightProjection.invert(ci.rightProjection) ;
+ }
+ ci.updateInverseProjection = false ;
+ }
+ }
+
+ /**
+ * Gets the transforms from clipping coordinates to view platform
+ * coordinates and copies them into the given Transform3Ds. These
+ * transforms take the clip space volume bounded by the range
+ * [-1.0 .. +1.0] on each of the X, Y, and Z axes and project into
+ * the view platform coordinate system.<p>
+ *
+ * With a monoscopic canvas the projection transform is copied to the
+ * first argument and the second argument is not used. For a stereo
+ * canvas the first argument receives the left projection transform, and
+ * if the second argument is non-null it receives the right projection
+ * transform.<p>
+ *
+ * If either of the clip policies <code>VIRTUAL_EYE</code> or
+ * <code>VIRTUAL_SCREEN</code> are used, then the View should be attached
+ * to a ViewPlatform that is part of a live scene graph and that has its
+ * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set; otherwise, a
+ * scale factor of 1.0 will be used for the scale factor from virtual
+ * world units to view platform units.
+ *
+ * @param c3d the Canvas3D to use
+ * @param cc2vpl the Transform3D to receive left transform
+ * @param cc2vpr the Transform3D to receive right transform, or null
+ */
+ public void getInverseViewPlatformProjection(Canvas3D c3d,
+ Transform3D cc2vpl,
+ Transform3D cc2vpr) {
+
+ CanvasInfo ci = updateCache
+ (c3d, "getInverseViewPlatformProjection", true) ;
+
+ getInverseViewPlatformProjection(ci) ;
+ cc2vpl.set(ci.inverseViewPlatformProjection) ;
+ if (ci.useStereo & cc2vpr != null)
+ cc2vpr.set(ci.inverseViewPlatformRightProjection) ;
+ }
+
+ private void getInverseViewPlatformProjection(CanvasInfo ci) {
+ if (ci.updateInverseViewPlatformProjection) {
+ if (verbose) System.err.println("updating InverseVpProjection") ;
+ if (ci.inverseViewPlatformProjection == null)
+ ci.inverseViewPlatformProjection = new Transform3D() ;
+
+ getInverseProjection(ci) ;
+ getEyeToViewPlatform(ci) ;
+ ci.inverseViewPlatformProjection.mul
+ (ci.eyeToViewPlatform, ci.inverseProjection) ;
+
+ if (ci.useStereo) {
+ if (ci.inverseViewPlatformRightProjection == null)
+ ci.inverseViewPlatformRightProjection = new Transform3D() ;
+
+ ci.inverseViewPlatformRightProjection.mul
+ (ci.rightEyeToViewPlatform, ci.inverseRightProjection) ;
+ }
+ ci.updateInverseVworldProjection = false ;
+ }
+ }
+
+ /**
+ * Gets the transforms from clipping coordinates to virtual world
+ * coordinates and copies them into the given Transform3Ds. These
+ * transforms take the clip space volume bounded by the range
+ * [-1.0 .. +1.0] on each of the X, Y, and Z axes and project into
+ * the virtual world.<p>
+ *
+ * With a monoscopic canvas the projection transform is copied to the
+ * first argument and the second argument is not used. For a stereo
+ * canvas the first argument receives the left projection transform, and
+ * if the second argument is non-null it receives the right projection
+ * transform.<p>
+ *
+ * The View must be attached to a ViewPlatform which is part of a live
+ * scene graph, and the ViewPlatform node must have its
+ * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set.
+ *
+ * @param c3d the Canvas3D to use
+ * @param cc2vwl the Transform3D to receive left transform
+ * @param cc2vwr the Transform3D to receive right transform, or null
+ */
+ public void getInverseVworldProjection(Canvas3D c3d,
+ Transform3D cc2vwl,
+ Transform3D cc2vwr) {
+
+ CanvasInfo ci = updateCache(c3d, "getInverseVworldProjection", true) ;
+ getInverseVworldProjection(ci) ;
+ cc2vwl.set(ci.inverseVworldProjection) ;
+ if (ci.useStereo & cc2vwr != null)
+ cc2vwr.set(ci.inverseVworldRightProjection) ;
+ }
+
+ private void getInverseVworldProjection(CanvasInfo ci) {
+ if (ci.updateInverseVworldProjection) {
+ if (verbose) System.err.println("updating InverseVwProjection") ;
+ if (ci.inverseVworldProjection == null)
+ ci.inverseVworldProjection = new Transform3D() ;
+
+ getInverseViewPlatformProjection(ci) ;
+ ci.inverseVworldProjection.mul
+ (vpi.viewPlatformToVworld, ci.inverseViewPlatformProjection) ;
+
+ if (ci.useStereo) {
+ if (ci.inverseVworldRightProjection == null)
+ ci.inverseVworldRightProjection = new Transform3D() ;
+
+ ci.inverseVworldRightProjection.mul
+ (vpi.viewPlatformToVworld,
+ ci.inverseViewPlatformRightProjection) ;
+ }
+ ci.updateInverseVworldProjection = false ;
+ }
+ }
+
+ //
+ // Compute a projection matrix from the given eye position in image plate,
+ // the front and back clip Z positions in image plate, and the current
+ // canvas position in image plate.
+ //
+ private void computeProjection(CanvasInfo ci, Point3d eye,
+ double front, double back, Transform3D p) {
+
+ // Convert everything to eye coordinates.
+ double lx = ci.canvasX - eye.x ; // left (low x)
+ double ly = ci.canvasY - eye.y ; // bottom (low y)
+ double hx = (ci.canvasX+ci.canvasWidth) - eye.x ; // right (high x)
+ double hy = (ci.canvasY+ci.canvasHeight) - eye.y ; // top (high y)
+ double nz = front - eye.z ; // front (near z)
+ double fz = back - eye.z ; // back (far z)
+ double iz = -eye.z ; // plate (image z)
+
+ if (projectionPolicy == View.PERSPECTIVE_PROJECTION)
+ computePerspectiveProjection(lx, ly, hx, hy, iz, nz, fz, m16d) ;
+ else
+ computeParallelProjection(lx, ly, hx, hy, nz, fz, m16d) ;
+
+ p.set(m16d) ;
+ }
+
+ //
+ // Compute a perspective projection from the given eye-space bounds.
+ //
+ private void computePerspectiveProjection(double lx, double ly,
+ double hx, double hy,
+ double iz, double nz,
+ double fz, double[] m) {
+ //
+ // We first derive the X and Y projection components without regard
+ // for Z scaling. The Z scaling or perspective depth is handled by
+ // matrix elements expressed solely in terms of the near and far clip
+ // planes.
+ //
+ // Since the eye is at the origin, the projector for any point V in
+ // eye space is just V. Any point along this ray can be expressed in
+ // parametric form as P = tV. To find the projection onto the plane
+ // containing the canvas, find t such that P.z = iz; ie, t = iz/V.z.
+ // The projection P is thus [V.x*iz/V.z, V.y*iz/V.z, iz].
+ //
+ // This projection can expressed as the following matrix equation:
+ //
+ // -iz 0 0 0 V.x
+ // 0 -iz 0 0 X V.y
+ // 0 0 -iz 0 V.z
+ // 0 0 -1 0 1 {matrix 1}
+ //
+ // where the matrix elements have been negated so that w is positive.
+ // This is mostly by convention, although some hardware won't handle
+ // clipping in the -w half-space.
+ //
+ // After the point has been projected to the image plate, the
+ // canvas bounds need to be mapped to the [-1..1] of Java 3D's
+ // clipping space. The scale factor for X is thus 2/(hx - lx); adding
+ // the translation results in (V.x - lx)(2/(hx - lx)) - 1, which after
+ // some algebra can be confirmed to the same as the following
+ // canonical scale/offset form:
+ //
+ // V.x*2/(hx - lx) - (hx + lx)/(hx - lx)
+ //
+ // Similarly for Y:
+ //
+ // V.y*2/(hy - ly) - (hy + ly)/(hy - ly)
+ //
+ // If we set idx = 1/(hx - lx) and idy = 1/(hy - ly), then we get:
+ //
+ // 2*V.x*idx - (hx + lx)idx
+ // 2*V.y*idy - (hy + ly)idy
+ //
+ // These scales and offsets are represented by the following matrix:
+ //
+ // 2*idx 0 0 -(hx + lx)*idx
+ // 0 2*idy 0 -(hy + ly)*idy
+ // 0 0 1 0
+ // 0 0 0 1 {matrix 2}
+ //
+ // The result after concatenating the projection transform
+ // ({matrix 2} X {matrix 1}):
+ //
+ // -2*iz*idx 0 (hx + lx)*idx 0
+ // 0 -2*iz*idy (hy + ly)*idy 0
+ // 0 0 -iz {a} 0 {b}
+ // 0 0 -1 0 {matrix 3}
+ //
+ // The Z scaling is handled by m[10] ("a") and m[11] ("b"), which must
+ // map the range [front..back] to [1..-1] in clipping space. If ze is
+ // the Z coordinate in eye space, and zc is the Z coordinate in
+ // clipping space after division by w, then from {matrix 3}:
+ //
+ // zc = (a*ze + b)/-ze = -(a + b/ze)
+ //
+ // We want this to map to +1 when ze is at the near clip plane, and
+ // to -1 when ze is at the far clip plane:
+ //
+ // -(a + b/nz) = +1
+ // -(a + b/fz) = -1
+ //
+ // Solving results in:
+ //
+ // a = -(nz + fz)/(nz - fz)
+ // b = (2*nz*fz)/(nz - fz).
+ //
+ // NOTE: this produces a perspective transform that has matrix
+ // components with a different scale than the matrix computed by the
+ // Java 3D core. They do in fact effect the equivalent clipping in 4D
+ // homogeneous coordinates and project to the same 3D Euclidean
+ // coordinates. m[14] is always -1 in our derivation above. If the
+ // matrix components produced by Java 3D core are divided by its value
+ // of -m[14], then both matrices are the same.
+ //
+ double idx = 1.0 / (hx - lx) ;
+ double idy = 1.0 / (hy - ly) ;
+ double idz = 1.0 / (nz - fz) ;
+
+ m[0] = -2.0 * iz * idx ;
+ m[5] = -2.0 * iz * idy ;
+ m[2] = (hx + lx) * idx ;
+ m[6] = (hy + ly) * idy ;
+ m[10] = -(nz + fz) * idz ;
+ m[11] = 2.0 * fz * nz * idz ;
+ m[14] = -1.0 ;
+ m[1] = m[3] = m[4] = m[7] = m[8] = m[9] = m[12] = m[13] = m[15] = 0.0 ;
+ }
+
+ //
+ // Compute a parallel projection from the given eye-space bounds.
+ //
+ private void computeParallelProjection(double lx, double ly,
+ double hx, double hy,
+ double nz, double fz, double[] m) {
+ //
+ // A parallel projection in eye space just involves scales and offsets
+ // with no w division. We can use {matrix 2} for the X and Y scales
+ // and offsets and then use a linear mapping of the front and back
+ // clip distances to the [1..-1] Z clip range.
+ //
+ double idx = 1.0 / (hx - lx) ;
+ double idy = 1.0 / (hy - ly) ;
+ double idz = 1.0 / (nz - fz) ;
+
+ m[0] = 2.0 * idx ;
+ m[5] = 2.0 * idy ;
+ m[10] = 2.0 * idz ;
+ m[3] = -(hx + lx) * idx ;
+ m[7] = -(hy + ly) * idy ;
+ m[11] = -(nz + fz) * idz ;
+ m[15] = 1.0 ;
+ m[1] = m[2] = m[4] = m[6] = m[8] = m[9] = m[12] = m[13] = m[14] = 0.0 ;
+ }
+
+ //
+ // Get front clip plane Z coordinate in image plate space.
+ //
+ private double getFrontClip(CanvasInfo ci, Point3d eye) {
+ if (frontClipPolicy == View.PHYSICAL_EYE ||
+ frontClipPolicy == View.VIRTUAL_EYE) {
+ return eye.z - ci.frontClipDistance ;
+ }
+ else {
+ return - ci.frontClipDistance ;
+ }
+ }
+
+ //
+ // Get back clip plane Z coordinate in image plate space.
+ //
+ private double getBackClip(CanvasInfo ci, Point3d eye) {
+ //
+ // Note: Clip node status is unavailable here. If a clip node is
+ // active in the scene graph, it should override the view's back
+ // clip plane.
+ //
+ if (backClipPolicy == View.PHYSICAL_EYE ||
+ backClipPolicy == View.VIRTUAL_EYE) {
+ return eye.z - ci.backClipDistance ;
+ }
+ else {
+ return -ci.backClipDistance ;
+ }
+ }
+
+ //
+ // Compute clip distance scale.
+ //
+ private double getClipScale(CanvasInfo ci, int clipPolicy) {
+ if (clipPolicy == View.VIRTUAL_EYE ||
+ clipPolicy == View.VIRTUAL_SCREEN) {
+ getScreenScale(ci) ;
+ if (resizePolicy == View.PHYSICAL_WORLD)
+ return vpi.vworldToViewPlatformScale * ci.screenScale *
+ ci.windowScale ;
+ else
+ return vpi.vworldToViewPlatformScale * ci.screenScale ;
+ }
+ else {
+ if (resizePolicy == View.PHYSICAL_WORLD)
+ return ci.windowScale ; // see below
+ else
+ return 1.0 ;
+ }
+ }
+
+ /**
+ * Gets the front clip distance scaled to physical meters. This is useful
+ * for ensuring that objects positioned relative to a physical coordinate
+ * system (such as eye, image plate, or coexistence) will be within the
+ * viewable Z depth. This distance will be relative to either the eye or
+ * the image plate depending upon the front clip policy.<p>
+ *
+ * Note that this is not necessarily the clip distance as set by
+ * <code>setFrontClipDistance</code>, even when the front clip policy
+ * is <code>PHYSICAL_SCREEN</code> or <code>PHYSICAL_EYE</code>. <i>If
+ * the window resize policy is <code>PHYSICAL_WORLD</code>, then physical
+ * clip distances as specified are in fact scaled by the ratio of the
+ * window width to the screen width.</i> The Java 3D view model does this
+ * to prevent the physical clip planes from moving with respect to the
+ * virtual world when the window is resized.<p>
+ *
+ * If either of the clip policies <code>VIRTUAL_EYE</code> or
+ * <code>VIRTUAL_SCREEN</code> are used, then the View should be attached
+ * to a ViewPlatform that is part of a live scene graph and that has its
+ * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set; otherwise, a
+ * scale factor of 1.0 will be used for the scale factor from virtual
+ * world units to view platform units.
+ *
+ * @param c3d the Canvas3D to use
+ * @return the physical front clip distance
+ */
+ public double getPhysicalFrontClipDistance(Canvas3D c3d) {
+ CanvasInfo ci = updateCache
+ (c3d, "getPhysicalFrontClipDistance", true) ;
+
+ getClipDistances(ci) ;
+ return ci.frontClipDistance ;
+ }
+
+ /**
+ * Gets the back clip distance scaled to physical meters. This is useful
+ * for ensuring that objects positioned relative to a physical coordinate
+ * system (such as eye, image plate, or coexistence) will be within the
+ * viewable Z depth. This distance will be relative to either the eye or
+ * the image plate depending upon the back clip policy.<p>
+ *
+ * Note that this is not necessarily the clip distance as set by
+ * <code>setBackClipDistance</code>, even when the back clip policy
+ * is <code>PHYSICAL_SCREEN</code> or <code>PHYSICAL_EYE</code>. <i>If
+ * the window resize policy is <code>PHYSICAL_WORLD</code>, then physical
+ * clip distances as specified are in fact scaled by the ratio of the
+ * window width to the screen width.</i> The Java 3D view model does this
+ * to prevent the physical clip planes from moving with respect to the
+ * virtual world when the window is resized.<p>
+ *
+ * If either of the clip policies <code>VIRTUAL_EYE</code> or
+ * <code>VIRTUAL_SCREEN</code> are used, then the View should be attached
+ * to a ViewPlatform that is part of a live scene graph and that has its
+ * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set; otherwise, a
+ * scale factor of 1.0 will be used for the scale factor from virtual
+ * world units to view platform units.
+ *
+ * @param c3d the Canvas3D to use
+ * @return the physical back clip distance
+ */
+ public double getPhysicalBackClipDistance(Canvas3D c3d) {
+ CanvasInfo ci = updateCache(c3d, "getPhysicalBackClipDistance", true) ;
+ getClipDistances(ci) ;
+ return ci.backClipDistance ;
+ }
+
+ private void getClipDistances(CanvasInfo ci) {
+ if (ci.updateClipDistances) {
+ if (verbose) System.err.println("updating clip distances") ;
+
+ ci.frontClipDistance = view.getFrontClipDistance() *
+ getClipScale(ci, frontClipPolicy) ;
+
+ ci.backClipDistance = view.getBackClipDistance() *
+ getClipScale(ci, backClipPolicy) ;
+
+ ci.updateClipDistances = false ;
+ if (verbose) {
+ System.err.println
+ (" front clip distance " + ci.frontClipDistance) ;
+ System.err.println
+ (" back clip distance " + ci.backClipDistance) ;
+ }
+ }
+ }
+
+ private void getScreenScale(CanvasInfo ci) {
+ if (ci.updateScreenScale) {
+ if (verbose) System.err.println("updating screen scale") ;
+
+ if (scalePolicy == View.SCALE_SCREEN_SIZE)
+ ci.screenScale = ci.si.screenWidth / 2.0 ;
+ else
+ ci.screenScale = view.getScreenScale() ;
+
+ ci.updateScreenScale = false ;
+ if (verbose) System.err.println("screen scale " + ci.screenScale) ;
+ }
+ }
+
+ /**
+ * Gets the scale factor from physical meters to view platform units.<p>
+ *
+ * This method requires a Canvas3D. A different scale may be returned
+ * for each canvas in the view if any of the following apply:<p><ul>
+ *
+ * <li>The window resize policy is <code>PHYSICAL_WORLD</code>, which
+ * alters the scale depending upon the width of the canvas.</li><p>
+ *
+ * <li>The screen scale policy is <code>SCALE_SCREEN_SIZE</code>,
+ * which alters the scale depending upon the width of the screen
+ * associated with the canvas.</li></ul>
+ *
+ * @param c3d the Canvas3D to use
+ * @return the physical to view platform scale
+ */
+ public double getPhysicalToViewPlatformScale(Canvas3D c3d) {
+ CanvasInfo ci = updateCache
+ (c3d, "getPhysicalToViewPlatformScale", false) ;
+
+ getPhysicalToViewPlatformScale(ci) ;
+ return ci.physicalToVpScale ;
+ }
+
+ private void getPhysicalToViewPlatformScale(CanvasInfo ci) {
+ if (ci.updatePhysicalToVpScale) {
+ if (verbose) System.err.println("updating PhysicalToVp scale") ;
+
+ getScreenScale(ci) ;
+ if (resizePolicy == View.PHYSICAL_WORLD)
+ ci.physicalToVpScale = 1.0/(ci.screenScale * ci.windowScale) ;
+ else
+ ci.physicalToVpScale = 1.0/ci.screenScale ;
+
+ ci.updatePhysicalToVpScale = false ;
+ if (verbose) System.err.println("PhysicalToVp scale " +
+ ci.physicalToVpScale) ;
+ }
+ }
+
+ /**
+ * Gets the scale factor from physical meters to virtual units.<p>
+ *
+ * This method requires a Canvas3D. A different scale may be returned
+ * across canvases for the same reasons as discussed in the description of
+ * <code>getPhysicalToViewPlatformScale</code>.<p>
+ *
+ * The View must be attached to a ViewPlatform which is part of a live
+ * scene graph, and the ViewPlatform node must have its
+ * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set.
+ *
+ * @param c3d the Canvas3D to use
+ * @return the physical to virtual scale
+ * @see #getPhysicalToViewPlatformScale
+ * getPhysicalToViewPlatformScale(Canvas3D)
+ */
+ public double getPhysicalToVirtualScale(Canvas3D c3d) {
+ CanvasInfo ci = updateCache(c3d, "getPhysicalToVirtualScale", true) ;
+ getPhysicalToVirtualScale(ci) ;
+ return ci.physicalToVirtualScale ;
+ }
+
+ private void getPhysicalToVirtualScale(CanvasInfo ci) {
+ if (ci.updatePhysicalToVirtualScale) {
+ if (verbose)
+ System.err.println("updating PhysicalToVirtual scale") ;
+
+ getPhysicalToViewPlatformScale(ci) ;
+ ci.physicalToVirtualScale =
+ ci.physicalToVpScale / vpi.vworldToViewPlatformScale ;
+
+ ci.updatePhysicalToVirtualScale = false ;
+ if (verbose) System.err.println("PhysicalToVirtual scale " +
+ ci.physicalToVirtualScale) ;
+ }
+ }
+
+ /**
+ * Gets the width of the specified canvas scaled to physical meters. This
+ * is derived from the physical screen width as reported by the Screen3D
+ * associated with the canvas. If the screen width is not explicitly set
+ * using the <code>setPhysicalScreenWidth</code> method of Screen3D, then
+ * Java 3D will derive the screen width based on a screen resolution of 90
+ * pixels/inch.
+ *
+ * @param c3d the Canvas3D to use
+ * @return the width of the canvas scaled to physical meters
+ */
+ public double getPhysicalWidth(Canvas3D c3d) {
+ CanvasInfo ci = updateCache(c3d, "getPhysicalWidth", false) ;
+ return ci.canvasWidth ;
+ }
+
+ /**
+ * Gets the height of the specified canvas scaled to physical meters. This
+ * is derived from the physical screen height as reported by the Screen3D
+ * associated with the canvas. If the screen height is not explicitly set
+ * using the <code>setPhysicalScreenHeight</code> method of Screen3D, then
+ * Java 3D will derive the screen height based on a screen resolution of 90
+ * pixels/inch.
+ *
+ * @param c3d the Canvas3D to use
+ * @return the height of the canvas scaled to physical meters
+ */
+ public double getPhysicalHeight(Canvas3D c3d) {
+ CanvasInfo ci = updateCache(c3d, "getPhysicalHeight", false) ;
+ return ci.canvasHeight ;
+ }
+
+ /**
+ * Gets the location of the specified canvas relative to the image plate
+ * origin. This is derived from the physical screen parameters as
+ * reported by the Screen3D associated with the canvas. If the screen
+ * width and height are not explicitly set in Screen3D, then Java 3D will
+ * derive those screen parameters based on a screen resolution of 90
+ * pixels/inch.
+ *
+ * @param c3d the Canvas3D to use
+ * @param location the output position, in meters, of the lower-left
+ * corner of the canvas relative to the image plate lower-left corner; Z
+ * is always 0.0
+ */
+ public void getPhysicalLocation(Canvas3D c3d, Point3d location) {
+ CanvasInfo ci = updateCache(c3d, "getPhysicalLocation", false) ;
+ location.set(ci.canvasX, ci.canvasY, 0.0) ;
+ }
+
+ /**
+ * Gets the location of the AWT pixel value and copies it into the
+ * specified Point3d.
+ *
+ * @param c3d the Canvas3D to use
+ * @param x the X coordinate of the pixel relative to the upper-left
+ * corner of the canvas
+ * @param y the Y coordinate of the pixel relative to the upper-left
+ * corner of the canvas
+ * @param location the output position, in meters, relative to the
+ * lower-left corner of the image plate; Z is always 0.0
+ */
+ public void getPixelLocationInImagePlate(Canvas3D c3d, int x, int y,
+ Point3d location) {
+
+ CanvasInfo ci = updateCache
+ (c3d, "getPixelLocationInImagePlate", false) ;
+
+ location.set(ci.canvasX + ((double)x * ci.si.metersPerPixelX),
+ ci.canvasY - ((double)y * ci.si.metersPerPixelY) +
+ ci.canvasHeight, 0.0) ;
+ }
+
+ /**
+ * Gets a read from the specified sensor and transforms it to virtual
+ * world coordinates. The View must be attached to a ViewPlatform which
+ * is part of a live scene graph, and the ViewPlatform node must have its
+ * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set.<p>
+ *
+ * This method requires a Canvas3D. The returned transform may differ
+ * across canvases for the same reasons as discussed in the description of
+ * <code>getViewPlatformToCoexistence</code>.
+ *
+ * @param sensor the Sensor instance to read
+ * @param s2vw the output transform
+ * @see #getViewPlatformToCoexistence
+ * getViewPlatformToCoexistence(Canvas3D, Transform3D)
+ */
+ public void getSensorToVworld(Canvas3D c3d,
+ Sensor sensor, Transform3D s2vw) {
+
+ CanvasInfo ci = updateCache(c3d, "getSensorToVworld", true) ;
+ getTrackerBaseToVworld(ci) ;
+ sensor.getRead(s2vw) ;
+ s2vw.mul(ci.trackerBaseToVworld, s2vw) ;
+ }
+
+ /**
+ * Gets the transform from tracker base coordinates to view platform
+ * coordinates and copies it into the specified Transform3D.<p>
+ *
+ * This method requires a Canvas3D. The returned transform may differ
+ * across canvases for the same reasons as discussed in the description of
+ * <code>getViewPlatformToCoexistence</code>.
+ *
+ * @param c3d the Canvas3D to use
+ * @param tb2vp the output transform
+ * @see #getViewPlatformToCoexistence
+ * getViewPlatformToCoexistence(Canvas3D, Transform3D)
+ */
+ public void getTrackerBaseToViewPlatform(Canvas3D c3d, Transform3D tb2vp) {
+ CanvasInfo ci = updateCache
+ (c3d, "getTrackerBaseToViewPlatform", false) ;
+
+ getTrackerBaseToViewPlatform(ci) ;
+ tb2vp.set(ci.trackerBaseToViewPlatform) ;
+ }
+
+ private void getTrackerBaseToViewPlatform(CanvasInfo ci) {
+ if (ci.updateTrackerBaseToViewPlatform) {
+ if (verbose) System.err.println("updating TrackerBaseToVp") ;
+ if (ci.trackerBaseToViewPlatform == null)
+ ci.trackerBaseToViewPlatform = new Transform3D() ;
+
+ getViewPlatformToCoexistence(ci) ;
+ ci.trackerBaseToViewPlatform.mul(coeToTrackerBase,
+ ci.viewPlatformToCoe) ;
+
+ ci.trackerBaseToViewPlatform.invert() ;
+ ci.updateTrackerBaseToViewPlatform = false ;
+ if (verbose) t3dPrint(ci.trackerBaseToViewPlatform,
+ "TrackerBaseToViewPlatform") ;
+ }
+ }
+
+ /**
+ * Gets the transform from tracker base coordinates to virtual world
+ * coordinates and copies it into the specified Transform3D. The View
+ * must be attached to a ViewPlatform which is part of a live scene graph,
+ * and the ViewPlatform node must have its
+ * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set.<p>
+ *
+ * This method requires a Canvas3D. The returned transform may differ
+ * across canvases for the same reasons as discussed in the description of
+ * <code>getViewPlatformToCoexistence</code>.
+ *
+ * @param c3d the Canvas3D to use
+ * @param tb2vw the output transform
+ * @see #getViewPlatformToCoexistence
+ * getViewPlatformToCoexistence(Canvas3D, Transform3D)
+ */
+ public void getTrackerBaseToVworld(Canvas3D c3d, Transform3D tb2vw) {
+ CanvasInfo ci = updateCache(c3d, "getTrackerBaseToVworld", true) ;
+ getTrackerBaseToVworld(ci) ;
+ tb2vw.set(ci.trackerBaseToVworld) ;
+ }
+
+ private void getTrackerBaseToVworld(CanvasInfo ci) {
+ if (ci.updateTrackerBaseToVworld) {
+ if (verbose) System.err.println("updating TrackerBaseToVworld") ;
+ if (ci.trackerBaseToVworld == null)
+ ci.trackerBaseToVworld = new Transform3D() ;
+ //
+ // We compute trackerBaseToViewPlatform and compose with
+ // viewPlatformToVworld instead of computing imagePlateToVworld
+ // and composing with trackerBaseToImagePlate. That way it works
+ // with HMD and avoids the issue of choosing the left image plate
+ // or right image plate transform.
+ //
+ getTrackerBaseToViewPlatform(ci) ;
+ ci.trackerBaseToVworld.mul(vpi.viewPlatformToVworld,
+ ci.trackerBaseToViewPlatform) ;
+
+ ci.updateTrackerBaseToVworld = false ;
+ }
+ }
+
+ /**
+ * Release all static memory references held by ViewInfo, if any. These
+ * are the Screen3D and ViewPlatform maps shared by all existing ViewInfo
+ * instances if they're not provided by a constructor. Releasing the
+ * screen references effectively releases all canvas references in all
+ * ViewInfo instances as well.<p>
+ *
+ * It is safe to continue using existing ViewInfo instances after calling
+ * this method; the data in the released maps will be re-derived as
+ * needed.
+ */
+ public static synchronized void clear() {
+ Iterator i = staticVpMap.values().iterator() ;
+ while (i.hasNext()) ((ViewPlatformInfo)i.next()).clear() ;
+ staticVpMap.clear() ;
+
+ i = staticSiMap.values().iterator() ;
+ while (i.hasNext()) ((ScreenInfo)i.next()).clear() ;
+ staticSiMap.clear() ;
+ }
+
+ /**
+ * Arrange for an update of cached screen parameters. If automatic update
+ * has not been enabled, then this method should be called if any of the
+ * attributes of the Screen3D have changed. This method should also be
+ * called if the screen changes pixel resolution.
+ *
+ * @param s3d the Screen3D to update
+ */
+ public void updateScreen(Screen3D s3d) {
+ if (verbose) System.err.println("updateScreen") ;
+ ScreenInfo si = (ScreenInfo)screenMap.get(s3d) ;
+ if (si != null) si.updateScreen = true ;
+ }
+
+ /**
+ * Arrange for an update of cached canvas parameters. If automatic update
+ * has not been enabled, then this method should be called if any of the
+ * attributes of the Canvas3D have changed. These attributes include the
+ * canvas position and size, but do <i>not</i> include the attributes of
+ * the associated Screen3D, which are cached separately.
+ *
+ * @param c3d the Canvas3D to update
+ */
+ public void updateCanvas(Canvas3D c3d) {
+ if (verbose) System.err.println("updateCanvas") ;
+ CanvasInfo ci = (CanvasInfo)canvasMap.get(c3d) ;
+ if (ci != null) ci.updateCanvas = true ;
+ }
+
+ /**
+ * Arrange for an update of cached view parameters. If automatic update
+ * has not been enabled for the View, then this method should be called if
+ * any of the attributes of the View associated with this object have
+ * changed.<p>
+ *
+ * These do <i>not</i> include the attributes of the existing Canvas3D or
+ * Screen3D components of the View, but do include the attributes of all
+ * other components such as the PhysicalEnvironment and PhysicalBody, and
+ * all attributes of the attached ViewPlatform except for its
+ * <code>localToVworld</code> transform. The screen and canvas components
+ * as well as the ViewPlatform's <code>localToVworld</code> are cached
+ * separately.<p>
+ *
+ * This method should also be called if the ViewPlatform is replaced with
+ * another using the View's <code>attachViewPlatform</code> method, or if
+ * any of the <code>setCanvas3D</code>, <code>addCanvas3D</code>,
+ * <code>insertCanvas3D</code>, <code>removeCanvas3D</code>, or
+ * <code>removeAllCanvas3Ds</code> methods of View are called to change
+ * the View's canvas list.<p>
+ *
+ * Calling this method causes most transforms to be re-derived. It should
+ * be used only when necessary.
+ */
+ public void updateView() {
+ if (verbose) System.err.println("updateView") ;
+ this.updateView = true ;
+ }
+
+ /**
+ * Arrange for an update of the cached head position if head tracking is
+ * enabled. If automatic update has not enabled for the head position,
+ * then this method should be called anytime a new head position is to be
+ * read.
+ */
+ public void updateHead() {
+ if (verbose) System.err.println("updateHead") ;
+ this.updateHead = true ;
+ }
+
+ /**
+ * Arrange for an update of the cached <code>localToVworld</code>
+ * transform of the view platform. If automatic update has not been
+ * enabled for this transform, then this method should be called anytime
+ * the view platform has been repositioned in the virtual world and a
+ * transform involving virtual world coordinates is desired.<p>
+ *
+ * The View must be attached to a ViewPlatform which is part of a live
+ * scene graph, and the ViewPlatform node must have its
+ * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set.
+ */
+ public void updateViewPlatform() {
+ if (verbose) System.err.println("updateViewPlatform") ;
+ vpi.updateViewPlatformToVworld = true ;
+ }
+
+ //
+ // Set cache update bits based on auto update flags.
+ // VIEW_AUTO_UPDATE is handled in updateCache().
+ //
+ private void getAutoUpdate(CanvasInfo ci) {
+ if ((autoUpdateFlags & SCREEN_AUTO_UPDATE) != 0)
+ ci.si.updateScreen = true ;
+
+ if ((autoUpdateFlags & CANVAS_AUTO_UPDATE) != 0)
+ ci.updateCanvas = true ;
+
+ if ((autoUpdateFlags & PLATFORM_AUTO_UPDATE) != 0)
+ vpi.updateViewPlatformToVworld = true ;
+
+ if ((autoUpdateFlags & HEAD_AUTO_UPDATE) != 0)
+ this.updateHead = true ;
+ }
+
+ //
+ // Update any changed cached data. This takes a Canvas3D instance. The
+ // cache mechanism could have used a Canvas3D index into the View instead,
+ // but the direct reference is probably more convenient for applications.
+ //
+ private CanvasInfo updateCache(Canvas3D c3d, String name, boolean vworld) {
+ if (verbose) {
+ System.err.println("updateCache: " + name + " in " + hashCode()) ;
+ System.err.println(" canvas " + c3d.hashCode()) ;
+ }
+
+ // The View may have had Canvas3D instances added or removed, or may
+ // have been attached to a different ViewPlatform, so update the view
+ // before anything else.
+ if (updateView || (autoUpdateFlags & VIEW_AUTO_UPDATE) != 0)
+ getViewInfo() ;
+
+ // Now get the CanvasInfo to update.
+ CanvasInfo ci = (CanvasInfo)canvasMap.get(c3d) ;
+ if (ci == null)
+ throw new IllegalArgumentException
+ ("\nSpecified Canvas3D is not a component of the View") ;
+
+ // Check rest of autoUpdateFlags.
+ if (autoUpdate) getAutoUpdate(ci) ;
+
+ // Update the screen, canvas, view platform, and head caches.
+ if (ci.si.updateScreen)
+ ci.si.getScreenInfo() ;
+
+ if (ci.updateCanvas)
+ ci.getCanvasInfo() ;
+
+ if (vworld && vpi.updateViewPlatformToVworld)
+ vpi.getViewPlatformToVworld() ;
+
+ if (useTracking && updateHead)
+ getHeadInfo() ;
+
+ // Return the CanvasInfo instance.
+ return ci ;
+ }
+
+ //
+ // Get physical view parameters and derived data. This is a fairly
+ // heavyweight method -- everything gets marked for update since we don't
+ // currently track changes in individual view attributes. Fortunately
+ // there shouldn't be a need to call it very often.
+ //
+ private void getViewInfo() {
+ if (verbose) System.err.println(" getViewInfo") ;
+
+ // Check if an update of the Canvas3D collection is needed.
+ if (this.canvasCount != view.numCanvas3Ds()) {
+ this.canvasCount = view.numCanvas3Ds() ;
+ getCanvases() ;
+ }
+ else {
+ for (int i = 0 ; i < canvasCount ; i++) {
+ if (canvasMap.get(view.getCanvas3D(i)) != canvasInfo[i]) {
+ getCanvases() ;
+ break ;
+ }
+ }
+ }
+
+ // Update the ViewPlatform.
+ getViewPlatform() ;
+
+ // Update the PhysicalBody and PhysicalEnvironment.
+ this.body = view.getPhysicalBody() ;
+ this.env = view.getPhysicalEnvironment() ;
+
+ // Use the result of the possibly overridden method useHeadTracking()
+ // to determine if head tracking is to be used within ViewInfo.
+ this.useTracking = useHeadTracking() ;
+
+ // Get the head tracker only if really available.
+ if (view.getTrackingEnable() && env.getTrackingAvailable()) {
+ int headIndex = env.getHeadIndex() ;
+ this.headTracker = env.getSensor(headIndex) ;
+ }
+
+ // Get the new policies and update data derived from them.
+ this.viewPolicy = view.getViewPolicy() ;
+ this.projectionPolicy = view.getProjectionPolicy() ;
+ this.resizePolicy = view.getWindowResizePolicy() ;
+ this.movementPolicy = view.getWindowMovementPolicy() ;
+ this.eyePolicy = view.getWindowEyepointPolicy() ;
+ this.scalePolicy = view.getScreenScalePolicy() ;
+ this.backClipPolicy = view.getBackClipPolicy() ;
+ this.frontClipPolicy = view.getFrontClipPolicy() ;
+
+ if (useTracking || viewPolicy == View.HMD_VIEW) {
+ if (this.headToHeadTracker == null)
+ this.headToHeadTracker = new Transform3D() ;
+ if (this.headTrackerToTrackerBase == null)
+ this.headTrackerToTrackerBase = new Transform3D() ;
+
+ if (viewPolicy == View.HMD_VIEW) {
+ if (this.trackerBaseToHeadTracker == null)
+ this.trackerBaseToHeadTracker = new Transform3D() ;
+ if (this.coeToHeadTracker == null)
+ this.coeToHeadTracker = new Transform3D() ;
+ }
+ else {
+ if (this.headToTrackerBase == null)
+ this.headToTrackerBase = new Transform3D() ;
+ }
+
+ body.getLeftEyePosition(this.leftEyeInHead) ;
+ body.getRightEyePosition(this.rightEyeInHead) ;
+ body.getHeadToHeadTracker(this.headToHeadTracker) ;
+
+ if (verbose) {
+ System.err.println(" leftEyeInHead " + leftEyeInHead) ;
+ System.err.println(" rightEyeInHead " + rightEyeInHead) ;
+ t3dPrint(headToHeadTracker, " headToHeadTracker") ;
+ }
+ }
+
+ if (eyePolicy == View.RELATIVE_TO_WINDOW ||
+ eyePolicy == View.RELATIVE_TO_FIELD_OF_VIEW) {
+ body.getLeftEyePosition(this.leftEyeInHead) ;
+ body.getRightEyePosition(this.rightEyeInHead) ;
+ if (verbose) {
+ System.err.println(" leftEyeInHead " + leftEyeInHead) ;
+ System.err.println(" rightEyeInHead " + rightEyeInHead) ;
+ }
+ }
+
+ if ((env.getCoexistenceCenterInPworldPolicy() !=
+ View.NOMINAL_SCREEN) || (viewPolicy == View.HMD_VIEW))
+ this.coeCentering = false ;
+ else
+ this.coeCentering = view.getCoexistenceCenteringEnable() ;
+
+ if (!coeCentering || useTracking) {
+ if (this.coeToTrackerBase == null)
+ this.coeToTrackerBase = new Transform3D() ;
+
+ env.getCoexistenceToTrackerBase(this.coeToTrackerBase) ;
+ if (verbose) t3dPrint(coeToTrackerBase, " coeToTrackerBase") ;
+ }
+
+ if (backClipPolicy == View.VIRTUAL_EYE ||
+ backClipPolicy == View.VIRTUAL_SCREEN ||
+ frontClipPolicy == View.VIRTUAL_EYE ||
+ frontClipPolicy == View.VIRTUAL_SCREEN) {
+ this.clipVirtual = true ;
+ }
+ else {
+ this.clipVirtual = false ;
+ }
+
+ // Propagate view updates to each canvas.
+ for (int i = 0 ; i < canvasCount ; i++)
+ this.canvasInfo[i].updateViewDependencies() ;
+
+ this.updateView = false ;
+ if (verbose) {
+ System.err.println(" tracking " + useTracking) ;
+ System.err.println(" coeCentering " + coeCentering) ;
+ System.err.println(" clipVirtual " + clipVirtual) ;
+ }
+ }
+
+ //
+ // Each view can have multiple canvases, each with an associated screen.
+ // Each canvas is associated with only one view. Each screen can have
+ // multiple canvases that are used across multiple views. We rebuild the
+ // canvas info instead of trying to figure out what canvases have been
+ // added or removed from the view.
+ //
+ private void getCanvases() {
+ if (this.canvasInfo.length < canvasCount) {
+ this.canvasInfo = new CanvasInfo[canvasCount] ;
+ }
+
+ for (int i = 0 ; i < canvasCount ; i++) {
+ Canvas3D c3d = view.getCanvas3D(i) ;
+ Screen3D s3d = c3d.getScreen3D() ;
+
+ // Check if we have a new screen.
+ ScreenInfo si = (ScreenInfo)screenMap.get(s3d) ;
+ if (si == null) {
+ si = new ScreenInfo(s3d, c3d.getGraphicsConfiguration()) ;
+ screenMap.put(s3d, si) ;
+ }
+
+ // Check to see if we've encountered the screen so far in this
+ // loop over the view's canvases. If not, clear the screen's list
+ // of canvases for this ViewInfo.
+ if (newSet.add(si)) si.clear(this) ;
+
+ // Check if this is a new canvas.
+ CanvasInfo ci = (CanvasInfo)canvasMap.get(c3d) ;
+ if (ci == null) ci = new CanvasInfo(c3d, si) ;
+
+ // Add this canvas to the screen's list for this ViewInfo.
+ si.addCanvasInfo(this, ci) ;
+
+ // Add this canvas to the new canvas map and canvas array.
+ this.newMap.put(c3d, ci) ;
+ this.canvasInfo[i] = ci ;
+ }
+
+ // Null out old references if canvas count shrinks.
+ for (int i = canvasCount ; i < canvasInfo.length ; i++)
+ this.canvasInfo[i] = null ;
+
+ // Update the CanvasInfo map.
+ Map tmp = canvasMap ;
+ this.canvasMap = newMap ;
+ this.newMap = tmp ;
+
+ // Clear the temporary collections.
+ this.newMap.clear() ;
+ this.newSet.clear() ;
+ }
+
+ //
+ // Force the creation of new CanvasInfo instances. This is called when a
+ // screen is removed from the screen map.
+ //
+ private void clearCanvases() {
+ this.canvasCount = 0 ;
+ this.canvasMap.clear() ;
+ this.updateView = true ;
+ }
+
+ //
+ // Update the view platform. Each view can be attached to only one, but
+ // each view platform can have many views attached.
+ //
+ private void getViewPlatform() {
+ ViewPlatform vp = view.getViewPlatform() ;
+ if (vp == null)
+ throw new IllegalStateException
+ ("The View must be attached to a ViewPlatform") ;
+
+ ViewPlatformInfo tmpVpi =
+ (ViewPlatformInfo)viewPlatformMap.get(vp) ;
+
+ if (tmpVpi == null) {
+ // We haven't encountered this ViewPlatform before.
+ tmpVpi = new ViewPlatformInfo(vp) ;
+ viewPlatformMap.put(vp, tmpVpi) ;
+ }
+
+ if (this.vpi != tmpVpi) {
+ // ViewPlatform has changed. Could set an update flag here if it
+ // would be used, but updating the view updates everything anyway.
+ if (this.vpi != null) {
+ // Remove this ViewInfo from the list of Views attached to the
+ // old ViewPlatform.
+ this.vpi.removeViewInfo(this) ;
+ }
+ this.vpi = tmpVpi ;
+ this.vpi.addViewInfo(this) ;
+
+ // updateViewPlatformToVworld is initially set false since the
+ // capability to read the vworld transform may not be
+ // available. If it is, set it here.
+ if (vp.getCapability(Node.ALLOW_LOCAL_TO_VWORLD_READ)) {
+ this.vpi.updateViewPlatformToVworld = true ;
+ if (verbose) System.err.println(" vworld read allowed") ;
+ } else
+ if (verbose) System.err.println(" vworld read disallowed") ;
+ }
+ }
+
+ //
+ // Force the creation of a new ViewPlatformInfo when a view platform is
+ // removed from the view platform map.
+ //
+ private void clearViewPlatform() {
+ this.updateView = true ;
+ }
+
+ //
+ // Update vworld dependencies for this ViewInfo -- called by
+ // ViewPlatformInfo.getViewPlatformToVworld().
+ //
+ private void updateVworldDependencies() {
+ for (int i = 0 ; i < canvasCount ; i++)
+ this.canvasInfo[i].updateVworldDependencies() ;
+ }
+
+ /**
+ * Returns a reference to a Transform3D containing the current transform
+ * from head tracker coordinates to tracker base coordinates. It is only
+ * called if <code>useHeadTracking</code> returns true and a head position
+ * update is specified with <code>updateHead</code> or the
+ * <code>HEAD_AUTO_UPDATE</code> constructor flag.<p>
+ *
+ * The default implementation uses the head tracking sensor specified by
+ * the View's PhysicalEnvironment, and reads it by calling the sensor's
+ * <code>getRead</code> method directly. The result is a sensor reading
+ * that may have been taken at a slightly different time from the one used
+ * by the renderer. This method can be overridden to synchronize the two
+ * readings through an external mechanism.
+ *
+ * @return current head tracker to tracker base transform
+ * @see #useHeadTracking
+ * @see #updateHead
+ * @see #HEAD_AUTO_UPDATE
+ */
+ protected Transform3D getHeadTrackerToTrackerBase() {
+ headTracker.getRead(this.headTrackerToTrackerBase) ;
+ return this.headTrackerToTrackerBase ;
+ }
+
+ /**
+ * Returns <code>true</code> if head tracking should be used.<p>
+ *
+ * The default implementation returns <code>true</code> if the View's
+ * <code>getTrackingEnable</code> method and the PhysicalEnvironment's
+ * <code>getTrackingAvailable</code> method both return <code>true</code>.
+ * These are the same conditions under which the Java 3D renderer uses
+ * head tracking. This method can be overridden if there is any need to
+ * decouple the head tracking status of ViewInfo from the renderer.
+ *
+ * @return <code>true</code> if ViewInfo should use head tracking
+ */
+ protected boolean useHeadTracking() {
+ return view.getTrackingEnable() && env.getTrackingAvailable() ;
+ }
+
+ //
+ // Cache the current tracked head position and derived data.
+ //
+ private void getHeadInfo() {
+ if (verbose) System.err.println(" getHeadInfo") ;
+
+ this.headTrackerToTrackerBase = getHeadTrackerToTrackerBase() ;
+ if (viewPolicy == View.HMD_VIEW) {
+ this.trackerBaseToHeadTracker.invert(headTrackerToTrackerBase) ;
+ this.coeToHeadTracker.mul(trackerBaseToHeadTracker,
+ coeToTrackerBase) ;
+ }
+ else {
+ this.headToTrackerBase.mul(headTrackerToTrackerBase,
+ headToHeadTracker) ;
+ }
+ for (int i = 0 ; i < canvasCount ; i++)
+ this.canvasInfo[i].updateHeadDependencies() ;
+
+ this.updateHead = false ;
+ //
+ // The head position used by the Java 3D renderer isn't accessible
+ // in the public API. A head tracker generates continuous data, so
+ // getting the same sensor read as the renderer is unlikely.
+ //
+ // Possible workaround: for fixed screens, get the Java 3D
+ // renderer's version of plateToVworld and headToVworld by calling
+ // Canvas3D.getImagePlateToVworld() and View.getUserHeadToVworld().
+ // Although the vworld components will have frame latency, they can
+ // be cancelled out by inverting the former transform and
+ // multiplying by the latter, resulting in userHeadToImagePlate,
+ // which can then be transformed to tracker base coordinates.
+ //
+ // For head mounted displays, the head to image plate transforms are
+ // just calibration constants, so they're of no use. There are more
+ // involved workarounds possible, but one that may work for both fixed
+ // screens and HMD is to define a SensorInterposer class that extends
+ // Sensor. Take the View's head tracking sensor, use it to construct
+ // a SensorInterposer, and then replace the head tracking sensor with
+ // the SensorInterposer. SensorInterposer can then override the
+ // getRead() methods and thus control what the Java 3D renderer gets.
+ // getHeadTrackerToTrackerBase() is a protected method in ViewInfo
+ // which can be overridden to call a variant of getRead() so that
+ // calls from ViewInfo and from the renderer can be distinguished.
+ //
+ // Even if getting the same head position as used by the renderer is
+ // achieved, tracked eye space interactions with objects in the
+ // virtual world still can't be synchronized with rendering. This
+ // means that objects in the virtual world cannot be made to appear in
+ // a fixed position relative to the tracked head position without a
+ // frame lag between them.
+ //
+ // The reason for this is that the tracked head position used by the
+ // Java 3D renderer is updated asynchronously from scene graph
+ // updates. This is done to reduce latency between the user's
+ // position and the rendered image, which is directly related to the
+ // quality of the immersive virtual reality experience. So while an
+ // update to the scene graph may have a frame latency before it gets
+ // rendered, a change to the user's tracked position is always
+ // reflected in the current frame.
+ //
+ // This problem can't be fixed without eliminating the frame latency
+ // in the Java 3D internal state, although there are possible
+ // workarounds at the expense of increased user position latency.
+ // These involve disabling tracking, reading the head sensor directly,
+ // performing whatever eye space interactions are necessary with the
+ // virtual world (using the view platform's current localToVworld),
+ // and then propagating the head position change to the renderer
+ // manually through a behavior post mechanism that delays it by a
+ // frame.
+ //
+ // For example, with head tracking in a fixed screen environment (such
+ // as a CAVE), disable Java 3D head tracking and set the View's window
+ // eyepoint policy to RELATIVE_TO_COEXISTENCE. Read the sensor to get
+ // the head position relative to the tracker base, transform it to
+ // coexistence coordinates using the inverse of the value of the
+ // coexistenceToTrackerBase transform, and then set the eye positions
+ // manually with the View's set{Left,Right}ManualEyeInCoexistence
+ // methods. If these method calls are delayed through a behavior post
+ // mechanism, then they will be synchronized with the rendering of the
+ // scene graph updates.
+ //
+ // With a head mounted display the sensor can be read directly to get
+ // the head position relative to the tracker base. If Java 3D's head
+ // tracking is disabled, it uses identity for the current
+ // headTrackerToTrackerBase transform. It concatenates its inverse,
+ // trackerBaseToHeadTracker, with coexistenceToTrackerBase to get the
+ // image plate positions in coexistence; the former transform is
+ // inaccessible, but the latter can be set through the
+ // PhysicalEnvironment. So the workaround is to maintain a local copy
+ // with the real value of coexistenceToTrackerBase, but set the
+ // PhysicalEnvironment copy to the product of the real value and the
+ // trackerBaseToHeadTracker inverted from the sensor read. Like the
+ // CAVE example, this update to the View would have to be delayed in
+ // order to synchronize with scene graph updates.
+ //
+ // Another possibility is to put the Java 3D view model in
+ // compatibility mode, where it accepts vpcToEye and eyeToCc
+ // (projection) directly. The various view attributes can still be
+ // set and accessed, but will be ignored by the Java 3D view model.
+ // The ViewInfo methods can be used to compute the view and projection
+ // matrices, which can then be delayed to synchronize with the scene
+ // graph.
+ //
+ // Note that these workarounds could be used to make view-dependent
+ // scene graph updates consistent, but they still can't do anything
+ // about synchronizing the actual physical position of the user with
+ // the rendered images. That requires zero latency between position
+ // update and scene graph state.
+ //
+ // Still another possibility: extrapolate the position of the user
+ // into the next few frames from a sample of recently recorded
+ // positions. Unfortunately, that is also a very hard problem. The
+ // Java 3D Sensor API is designed to support prediction but it was
+ // never realized successfully in the sample implementation.
+ }
+
+ //
+ // A per-screen cache, shared between ViewInfo instances. In the Java 3D
+ // view model a single screen can be associated with multiple canvas
+ // and view instances.
+ //
+ private static class ScreenInfo {
+ private Screen3D s3d = null ;
+ private GraphicsConfiguration graphicsConfiguration = null ;
+ private boolean updateScreen = true ;
+
+ private Map viewInfoMap = new HashMap() ;
+ private List viewInfoList = new LinkedList() ;
+ private Transform3D t3d = new Transform3D() ;
+
+ private double screenWidth = 0.0 ;
+ private double screenHeight = 0.0 ;
+ private boolean updateScreenSize = true ;
+
+ private Rectangle screenBounds = null ;
+ private double metersPerPixelX = 0.0 ;
+ private double metersPerPixelY = 0.0 ;
+ private boolean updatePixelSize = true ;
+
+ // These transforms are pre-allocated here since they are required by
+ // some view policies and we don't know what views this screen will be
+ // attached to. Their default identity values are used if not
+ // explicitly set. TODO: allocate if needed in getCanvasInfo(), where
+ // view information will be available.
+ private Transform3D trackerBaseToPlate = new Transform3D() ;
+ private Transform3D headTrackerToLeftPlate = new Transform3D() ;
+ private Transform3D headTrackerToRightPlate = new Transform3D() ;
+ private boolean updateTrackerBaseToPlate = false ;
+ private boolean updateHeadTrackerToPlate = false ;
+
+ private ScreenInfo(Screen3D s3d, GraphicsConfiguration gc) {
+ this.s3d = s3d ;
+ this.graphicsConfiguration = gc ;
+ if (verbose)
+ System.err.println(" ScreenInfo: init " + s3d.hashCode()) ;
+ }
+
+ private List getCanvasList(ViewInfo vi) {
+ List canvasList = (List)viewInfoMap.get(vi) ;
+ if (canvasList == null) {
+ canvasList = new LinkedList() ;
+ viewInfoMap.put(vi, canvasList) ;
+ viewInfoList.add(canvasList) ;
+ }
+ return canvasList ;
+ }
+
+ private synchronized void clear(ViewInfo vi) {
+ getCanvasList(vi).clear() ;
+ }
+
+ private synchronized void clear() {
+ Iterator i = viewInfoMap.keySet().iterator() ;
+ while (i.hasNext()) ((ViewInfo)i.next()).clearCanvases() ;
+ viewInfoMap.clear() ;
+
+ i = viewInfoList.iterator() ;
+ while (i.hasNext()) ((List)i.next()).clear() ;
+ viewInfoList.clear() ;
+ }
+
+ private synchronized void addCanvasInfo(ViewInfo vi, CanvasInfo ci) {
+ getCanvasList(vi).add(ci) ;
+ }
+
+ //
+ // Get all relevant screen information, find out what changed, and
+ // flag derived data. With normal use it's unlikely that any of the
+ // Screen3D attributes will change after the first time this method is
+ // called. It's possible that the screen resolution changed or some
+ // sort of interactive screen calibration is in process.
+ //
+ private synchronized void getScreenInfo() {
+ if (verbose)
+ System.err.println(" getScreenInfo " + s3d.hashCode());
+
+ // This is used for positioning screens in relation to each other
+ // and must be accurate for good results with multi-screen
+ // displays. By default the coexistence to tracker base transform
+ // is identity so in that case this transform will also set the
+ // image plate in coexistence coordinates.
+ s3d.getTrackerBaseToImagePlate(t3d) ;
+ if (! t3d.equals(trackerBaseToPlate)) {
+ this.trackerBaseToPlate.set(t3d) ;
+ this.updateTrackerBaseToPlate = true ;
+ if (verbose) t3dPrint(trackerBaseToPlate,
+ " trackerBaseToPlate") ;
+ }
+
+ // This transform and the following are used for head mounted
+ // displays. They should be based on the *apparent* position of
+ // the screens as viewed through the HMD optics.
+ s3d.getHeadTrackerToLeftImagePlate(t3d) ;
+ if (! t3d.equals(headTrackerToLeftPlate)) {
+ this.headTrackerToLeftPlate.set(t3d) ;
+ this.updateHeadTrackerToPlate = true ;
+ if (verbose) t3dPrint(headTrackerToLeftPlate,
+ " headTrackerToLeftPlate") ;
+ }
+
+ s3d.getHeadTrackerToRightImagePlate(t3d) ;
+ if (! t3d.equals(headTrackerToRightPlate)) {
+ this.headTrackerToRightPlate.set(t3d) ;
+ this.updateHeadTrackerToPlate = true ;
+ if (verbose) t3dPrint(headTrackerToRightPlate,
+ " headTrackerToRightPlate") ;
+ }
+
+ // If the screen width and height in meters are not explicitly set
+ // through the Screen3D, then the Screen3D will assume a pixel
+ // resolution of 90 pixels/inch and compute the dimensions from
+ // the screen resolution. These dimensions should be measured
+ // accurately for multi-screen displays. For HMD, these
+ // dimensions should be the *apparent* width and height as viewed
+ // through the HMD optics.
+ double w = s3d.getPhysicalScreenWidth() ;
+ double h = s3d.getPhysicalScreenHeight();
+ if (w != screenWidth || h != screenHeight) {
+ this.screenWidth = w ;
+ this.screenHeight = h ;
+ this.updateScreenSize = true ;
+ if (verbose) {
+ System.err.println(" screen width " + screenWidth) ;
+ System.err.println(" screen height " + screenHeight) ;
+ }
+ }
+
+ this.screenBounds = graphicsConfiguration.getBounds() ;
+ double mpx = screenWidth / (double)screenBounds.width ;
+ double mpy = screenHeight / (double)screenBounds.height ;
+ if ((mpx != metersPerPixelX) || (mpy != metersPerPixelY)) {
+ this.metersPerPixelX = mpx ;
+ this.metersPerPixelY = mpy ;
+ this.updatePixelSize = true ;
+ if (verbose) {
+ System.err.println(" screen bounds " + screenBounds) ;
+ System.err.println(" pixel size X " + metersPerPixelX) ;
+ System.err.println(" pixel size Y " + metersPerPixelY) ;
+ }
+ }
+
+ // Propagate screen updates to each canvas in each ViewInfo.
+ Iterator vi = viewInfoList.iterator() ;
+ while (vi.hasNext()) {
+ Iterator ci = ((List)vi.next()).iterator() ;
+ while (ci.hasNext())
+ ((CanvasInfo)ci.next()).updateScreenDependencies() ;
+ }
+
+ this.updateTrackerBaseToPlate = false ;
+ this.updateHeadTrackerToPlate = false ;
+ this.updateScreenSize = false ;
+ this.updatePixelSize = false ;
+ this.updateScreen = false ;
+ }
+ }
+
+ //
+ // A per-ViewPlatform cache, shared between ViewInfo instances. In the
+ // Java 3D view model, a view platform may have several views attached to
+ // it. The only view platform data cached here is its localToVworld, the
+ // inverse of its localToVworld, and the scale from vworld to view
+ // platform coordinates. The view platform to coexistence transform is
+ // cached by the CanvasInfo instances associated with the ViewInfo.
+ //
+ private static class ViewPlatformInfo {
+ private ViewPlatform vp = null ;
+ private List viewInfo = new LinkedList() ;
+ private double[] m = new double[16] ;
+
+ // These transforms are pre-allocated since we don't know what views
+ // will be attached. Their default identity values are used if a
+ // vworld dependent computation is requested and no initial update of
+ // the view platform was performed; this occurs if the local to vworld
+ // read capability isn't set. TODO: rationalize this and allocate
+ // only if necessary.
+ private Transform3D viewPlatformToVworld = new Transform3D() ;
+ private Transform3D vworldToViewPlatform = new Transform3D() ;
+ private double vworldToViewPlatformScale = 1.0 ;
+
+ // Set these update flags initially false since we might not have the
+ // capability to read the vworld transform.
+ private boolean updateViewPlatformToVworld = false ;
+ private boolean updateVworldScale = false ;
+
+ private ViewPlatformInfo(ViewPlatform vp) {
+ this.vp = vp ;
+ if (verbose) System.err.println
+ (" ViewPlatformInfo: init " + vp.hashCode()) ;
+ }
+
+ private synchronized void addViewInfo(ViewInfo vi) {
+ this.viewInfo.add(vi) ;
+ }
+
+ private synchronized void removeViewInfo(ViewInfo vi) {
+ this.viewInfo.remove(vi) ;
+ }
+
+ private synchronized void clear() {
+ Iterator i = viewInfo.iterator() ;
+ while (i.hasNext()) ((ViewInfo)i.next()).clearViewPlatform() ;
+ viewInfo.clear() ;
+ }
+
+ //
+ // Get the view platform's current <code>localToVworld</code> and
+ // force the update of derived data.
+ //
+ private synchronized void getViewPlatformToVworld() {
+ if (verbose) System.err.println
+ (" getViewPlatformToVworld " + vp.hashCode()) ;
+
+ vp.getLocalToVworld(this.viewPlatformToVworld) ;
+ this.vworldToViewPlatform.invert(viewPlatformToVworld) ;
+
+ // Get the scale factor from the virtual world to view platform
+ // transform. Note that this is always a congruent transform.
+ vworldToViewPlatform.get(m) ;
+ double newScale = Math.sqrt(m[0]*m[0] + m[1]*m[1] + m[2]*m[2]) ;
+
+ // May need to update clip plane distances if scale changed. We'll
+ // check with an epsilon commensurate with single precision float.
+ // It would be more efficient to check the square of the distance
+ // and then compute the square root only if different, but that
+ // makes choosing an epsilon difficult.
+ if ((newScale > vworldToViewPlatformScale + 0.0000001) ||
+ (newScale < vworldToViewPlatformScale - 0.0000001)) {
+ this.vworldToViewPlatformScale = newScale ;
+ this.updateVworldScale = true ;
+ if (verbose) System.err.println(" vworld scale " +
+ vworldToViewPlatformScale) ;
+ }
+
+ // All virtual world transforms must be updated.
+ Iterator i = viewInfo.iterator() ;
+ while (i.hasNext())
+ ((ViewInfo)i.next()).updateVworldDependencies() ;
+
+ this.updateVworldScale = false ;
+ this.updateViewPlatformToVworld = false ;
+ }
+ }
+
+ //
+ // A per-canvas cache.
+ //
+ private class CanvasInfo {
+ private Canvas3D c3d = null ;
+ private ScreenInfo si = null ;
+ private boolean updateCanvas = true ;
+
+ private double canvasX = 0.0 ;
+ private double canvasY = 0.0 ;
+ private boolean updatePosition = true ;
+
+ private double canvasWidth = 0.0 ;
+ private double canvasHeight = 0.0 ;
+ private double windowScale = 0.0 ;
+ private boolean updateWindowScale = true ;
+
+ private double screenScale = 0.0 ;
+ private boolean updateScreenScale = true ;
+
+ private boolean useStereo = false ;
+ private boolean updateStereo = true ;
+
+ //
+ // coeToPlate is the same for each Canvas3D in a Screen3D unless
+ // coexistence centering is enabled and the window movement policy is
+ // PHYSICAL_WORLD.
+ //
+ private Transform3D coeToPlate = null ;
+ private Transform3D coeToRightPlate = null ;
+ private boolean updateCoeToPlate = true ;
+
+ //
+ // viewPlatformToCoe is the same for each Canvas3D in a View unless
+ // the window resize policy is PHYSICAL_WORLD, in which case the scale
+ // factor includes the window scale; or if the screen scale policy is
+ // SCALE_SCREEN_SIZE, in which case the scale factor depends upon the
+ // width of the screen associated with the canvas; or if the window
+ // eyepoint policy is RELATIVE_TO_FIELD_OF_VIEW and the view attach
+ // policy is not NOMINAL_SCREEN, which will set the view platform
+ // origin in coexistence based on the width of the canvas.
+ //
+ private Transform3D viewPlatformToCoe = null ;
+ private Transform3D coeToViewPlatform = null ;
+ private boolean updateViewPlatformToCoe = true ;
+ private boolean updateCoeToViewPlatform = true ;
+
+ //
+ // plateToViewPlatform is composed from viewPlatformToCoe and
+ // coeToPlate.
+ //
+ private Transform3D plateToViewPlatform = null ;
+ private Transform3D rightPlateToViewPlatform = null ;
+ private boolean updatePlateToViewPlatform = true ;
+
+ //
+ // trackerBaseToViewPlatform is computed from viewPlatformToCoe and
+ // coeToTrackerBase.
+ //
+ private Transform3D trackerBaseToViewPlatform = null ;
+ private boolean updateTrackerBaseToViewPlatform = true ;
+
+ //
+ // Eye position in image plate is always different for each Canvas3D
+ // in a View, unless two or more Canvas3D instances are the same
+ // position and size, or two or more Canvas3D instances are using a
+ // window eyepoint policy of RELATIVE_TO_SCREEN and have the same
+ // settings for the manual eye positions.
+ //
+ private Point3d eyeInPlate = new Point3d() ;
+ private Point3d rightEyeInPlate = new Point3d() ;
+ private Transform3D eyeToPlate = null ;
+ private Transform3D rightEyeToPlate = null ;
+ private boolean updateEyeInPlate = true ;
+
+ private Point3d leftManualEyeInPlate = new Point3d() ;
+ private Point3d rightManualEyeInPlate = new Point3d() ;
+ private boolean updateManualEye = true ;
+
+ private int monoscopicPolicy = -1 ;
+ private boolean updateMonoPolicy = true ;
+
+ //
+ // eyeToViewPlatform is computed from eyeToPlate and
+ // plateToViewPlatform.
+ //
+ private Transform3D eyeToViewPlatform = null ;
+ private Transform3D rightEyeToViewPlatform = null ;
+ private boolean updateEyeToViewPlatform = true ;
+
+ private Transform3D viewPlatformToEye = null ;
+ private Transform3D viewPlatformToRightEye = null ;
+ private boolean updateViewPlatformToEye = true ;
+
+ //
+ // The projection transform depends upon eye position in image plate.
+ //
+ private Transform3D projection = null ;
+ private Transform3D rightProjection = null ;
+ private boolean updateProjection = true ;
+
+ private Transform3D inverseProjection = null ;
+ private Transform3D inverseRightProjection = null ;
+ private boolean updateInverseProjection = true ;
+
+ private Transform3D inverseViewPlatformProjection = null ;
+ private Transform3D inverseViewPlatformRightProjection = null ;
+ private boolean updateInverseViewPlatformProjection = true ;
+
+ //
+ // The physical clip distances can be affected by the canvas width
+ // with the PHYSICAL_WORLD resize policy.
+ //
+ private double frontClipDistance = 0.0 ;
+ private double backClipDistance = 0.0 ;
+ private boolean updateClipDistances = true ;
+
+ //
+ // The physical to view platform scale can be affected by the canvas
+ // width with the PHYSICAL_WORLD resize policy.
+ //
+ private double physicalToVpScale = 0.0 ;
+ private double physicalToVirtualScale = 0.0 ;
+ private boolean updatePhysicalToVpScale = true ;
+ private boolean updatePhysicalToVirtualScale = true ;
+
+ //
+ // The vworld transforms require reading the ViewPlaform's
+ // localToVworld tranform.
+ //
+ private Transform3D plateToVworld = null ;
+ private Transform3D rightPlateToVworld = null ;
+ private boolean updatePlateToVworld = true ;
+
+ private Transform3D coeToVworld = null ;
+ private boolean updateCoeToVworld = true ;
+
+ private Transform3D eyeToVworld = null ;
+ private Transform3D rightEyeToVworld = null ;
+ private boolean updateEyeToVworld = true ;
+
+ private Transform3D trackerBaseToVworld = null ;
+ private boolean updateTrackerBaseToVworld = true ;
+
+ private Transform3D inverseVworldProjection = null ;
+ private Transform3D inverseVworldRightProjection = null ;
+ private boolean updateInverseVworldProjection = true ;
+
+ private CanvasInfo(Canvas3D c3d, ScreenInfo si) {
+ this.si = si ;
+ this.c3d = c3d ;
+ if (verbose) System.err.println(" CanvasInfo: init " +
+ c3d.hashCode()) ;
+ }
+
+ private void getCanvasInfo() {
+ if (verbose) System.err.println(" getCanvasInfo " +
+ c3d.hashCode()) ;
+ boolean newStereo =
+ c3d.getStereoEnable() && c3d.getStereoAvailable() ;
+
+ if (useStereo != newStereo) {
+ this.useStereo = newStereo ;
+ this.updateStereo = true ;
+ if (verbose) System.err.println(" stereo " + useStereo) ;
+ }
+
+ this.canvasWidth = c3d.getWidth() * si.metersPerPixelX ;
+ this.canvasHeight = c3d.getHeight() * si.metersPerPixelY ;
+ double newScale = canvasWidth / si.screenWidth ;
+
+ if (windowScale != newScale) {
+ this.windowScale = newScale ;
+ this.updateWindowScale = true ;
+ if (verbose) {
+ System.err.println(" width " + canvasWidth) ;
+ System.err.println(" height " + canvasHeight) ;
+ System.err.println(" scale " + windowScale) ;
+ }
+ }
+
+ // For multiple physical screens, AWT returns the canvas location
+ // relative to the origin of the aggregated virtual screen. We
+ // need the location relative to the physical screen origin.
+ Point awtLocation = c3d.getLocationOnScreen() ;
+ int x = awtLocation.x - si.screenBounds.x ;
+ int y = awtLocation.y - si.screenBounds.y ;
+
+ double newCanvasX = si.metersPerPixelX * x ;
+ double newCanvasY = si.metersPerPixelY *
+ (si.screenBounds.height - (y + c3d.getHeight())) ;
+
+ if (canvasX != newCanvasX || canvasY != newCanvasY) {
+ this.canvasX = newCanvasX ;
+ this.canvasY = newCanvasY ;
+ this.updatePosition = true ;
+ if (verbose) {
+ System.err.println(" lower left X " + canvasX) ;
+ System.err.println(" lower left Y " + canvasY) ;
+ }
+ }
+
+ int newMonoPolicy = c3d.getMonoscopicViewPolicy() ;
+ if (monoscopicPolicy != newMonoPolicy) {
+ this.monoscopicPolicy = newMonoPolicy ;
+ this.updateMonoPolicy = true ;
+
+ if (verbose && !useStereo) {
+ if (monoscopicPolicy == View.LEFT_EYE_VIEW)
+ System.err.println(" left eye view") ;
+ else if (monoscopicPolicy == View.RIGHT_EYE_VIEW)
+ System.err.println(" right eye view") ;
+ else
+ System.err.println(" cyclopean view") ;
+ }
+ }
+
+ c3d.getLeftManualEyeInImagePlate(leftEye) ;
+ c3d.getRightManualEyeInImagePlate(rightEye) ;
+
+ if (!leftEye.equals(leftManualEyeInPlate) ||
+ !rightEye.equals(rightManualEyeInPlate)) {
+
+ this.leftManualEyeInPlate.set(leftEye) ;
+ this.rightManualEyeInPlate.set(rightEye) ;
+ this.updateManualEye = true ;
+
+ if (verbose && (eyePolicy == View.RELATIVE_TO_WINDOW ||
+ eyePolicy == View.RELATIVE_TO_SCREEN)) {
+ System.err.println(" left manual eye in plate " +
+ leftManualEyeInPlate) ;
+ System.err.println(" right manual eye in plate " +
+ rightManualEyeInPlate) ;
+ }
+ }
+
+ updateCanvasDependencies() ;
+ this.updateStereo = false ;
+ this.updateWindowScale = false ;
+ this.updatePosition = false ;
+ this.updateMonoPolicy = false ;
+ this.updateManualEye = false ;
+ this.updateCanvas = false ;
+ }
+
+ private double getFieldOfViewOffset() {
+ return 0.5 * canvasWidth / Math.tan(0.5 * view.getFieldOfView()) ;
+ }
+
+ private void updateScreenDependencies() {
+ if (si.updatePixelSize || si.updateScreenSize) {
+ if (eyePolicy == View.RELATIVE_TO_WINDOW ||
+ eyePolicy == View.RELATIVE_TO_FIELD_OF_VIEW) {
+ // Physical location of the canvas might change without
+ // changing the pixel location.
+ updateEyeInPlate = true ;
+ }
+ if (resizePolicy == View.PHYSICAL_WORLD ||
+ eyePolicy == View.RELATIVE_TO_FIELD_OF_VIEW) {
+ // Could change the window scale or view platform Z offset.
+ updateViewPlatformToCoe = true ;
+ }
+ if (resizePolicy == View.PHYSICAL_WORLD) {
+ // Window scale affects the clip distance and the physical
+ // to viewplatform scale.
+ updateClipDistances = true ;
+ updatePhysicalToVpScale = true ;
+ updatePhysicalToVirtualScale = true ;
+ }
+ // Upper right corner of canvas may have moved from eye.
+ updateProjection = true ;
+ }
+ if (si.updateScreenSize && scalePolicy == View.SCALE_SCREEN_SIZE) {
+ // Screen scale affects the clip distances and physical to
+ // view platform scale. The screen scale is also a component
+ // of viewPlatformToCoe.
+ updateScreenScale = true ;
+ updateClipDistances = true ;
+ updatePhysicalToVpScale = true ;
+ updatePhysicalToVirtualScale = true ;
+ updateViewPlatformToCoe = true ;
+ }
+
+ if (viewPolicy == View.HMD_VIEW) {
+ if (si.updateHeadTrackerToPlate) {
+ // Plate moves with respect to the eye and coexistence.
+ updateEyeInPlate = true ;
+ updateCoeToPlate = true ;
+ }
+ }
+ else if (coeCentering) {
+ if (movementPolicy == View.PHYSICAL_WORLD) {
+ // Coexistence is centered on the canvas.
+ if (si.updatePixelSize || si.updateScreenSize)
+ // Physical location of the canvas might change
+ // without changing the pixel location.
+ updateCoeToPlate = true ;
+ }
+ else if (si.updateScreenSize)
+ // Coexistence is centered on the screen.
+ updateCoeToPlate = true ;
+ }
+ else if (si.updateTrackerBaseToPlate) {
+ // Image plate has possibly changed location. Could be
+ // offset by an update to coeToTrackerBase in the
+ // PhysicalEnvironment though.
+ updateCoeToPlate = true ;
+ }
+
+ if (updateCoeToPlate &&
+ eyePolicy == View.RELATIVE_TO_COEXISTENCE) {
+ // Coexistence has moved with respect to plate.
+ updateEyeInPlate = true ;
+ }
+ if (updateViewPlatformToCoe) {
+ // Derived transforms. trackerBaseToViewPlatform is composed
+ // from viewPlatformToCoe and coexistenceToTrackerBase.
+ updateCoeToViewPlatform = true ;
+ updateCoeToVworld = true ;
+ updateTrackerBaseToViewPlatform = true ;
+ updateTrackerBaseToVworld = true ;
+ }
+ if (updateCoeToPlate || updateViewPlatformToCoe) {
+ // The image plate to view platform transform is composed from
+ // the coexistence to image plate and view platform to
+ // coexistence transforms, so these need updates as well.
+ updatePlateToViewPlatform = true ;
+ updatePlateToVworld = true ;
+ }
+ updateEyeDependencies() ;
+ }
+
+ private void updateEyeDependencies() {
+ if (updateEyeInPlate) {
+ updateEyeToVworld = true ;
+ updateProjection = true ;
+ }
+ if (updateProjection) {
+ updateInverseProjection = true ;
+ updateInverseViewPlatformProjection = true ;
+ updateInverseVworldProjection = true ;
+ }
+ if (updateEyeInPlate || updatePlateToViewPlatform) {
+ updateViewPlatformToEye = true ;
+ updateEyeToViewPlatform = true ;
+ }
+ }
+
+ private void updateCanvasDependencies() {
+ if (updateStereo || updateMonoPolicy ||
+ (updateManualEye && (eyePolicy == View.RELATIVE_TO_WINDOW ||
+ eyePolicy == View.RELATIVE_TO_SCREEN))) {
+ updateEyeInPlate = true ;
+ }
+ if (updateWindowScale || updatePosition) {
+ if (coeCentering && movementPolicy == View.PHYSICAL_WORLD) {
+ // Coexistence is centered on the canvas.
+ updateCoeToPlate = true ;
+ if (eyePolicy == View.RELATIVE_TO_COEXISTENCE)
+ updateEyeInPlate = true ;
+ }
+ if (eyePolicy == View.RELATIVE_TO_FIELD_OF_VIEW ||
+ eyePolicy == View.RELATIVE_TO_WINDOW)
+ // Eye depends on canvas position and size.
+ updateEyeInPlate = true ;
+ }
+ if (updateWindowScale) {
+ if (resizePolicy == View.PHYSICAL_WORLD ||
+ eyePolicy == View.RELATIVE_TO_FIELD_OF_VIEW) {
+ // View platform scale and its origin Z offset changed.
+ // trackerBaseToViewPlatform needs viewPlatformToCoe.
+ updateViewPlatformToCoe = true ;
+ updateCoeToViewPlatform = true ;
+ updateCoeToVworld = true ;
+ updateTrackerBaseToViewPlatform = true ;
+ updateTrackerBaseToVworld = true ;
+ }
+ if (resizePolicy == View.PHYSICAL_WORLD) {
+ // Clip distance and physical to view platform scale are
+ // affected by the window size.
+ updateClipDistances = true ;
+ updateProjection = true ;
+ updatePhysicalToVpScale = true ;
+ updatePhysicalToVirtualScale = true ;
+ }
+ }
+ if (updateViewPlatformToCoe || updateCoeToPlate) {
+ // The image plate to view platform transform is composed from
+ // the coexistence to image plate and the view platform to
+ // coexistence transforms, so these need updates.
+ updatePlateToViewPlatform = true ;
+ updatePlateToVworld = true ;
+ }
+ if (coeCentering && !updateManualEye && !updateWindowScale &&
+ (movementPolicy == View.PHYSICAL_WORLD) &&
+ (eyePolicy != View.RELATIVE_TO_SCREEN)) {
+ // The canvas may have moved, but the eye, coexistence, and
+ // view platform moved with it. updateEyeDependencies()
+ // isn't called since it would unnecessarily update the
+ // projection and eyeToViewPlatform transforms. The tested
+ // policies are all true by default.
+ return ;
+ }
+ updateEyeDependencies() ;
+ }
+
+ //
+ // TODO: A brave soul could refine cache updates here. There are a
+ // lot of attributes to monitor, so we just update everything for now.
+ //
+ private void updateViewDependencies() {
+ // View policy, physical body eye positions, head to head
+ // tracker, window eyepoint policy, field of view, coexistence
+ // centering, or coexistence to image plate may have changed.
+ updateEyeInPlate = true ;
+
+ // If the eye position in image plate has changed, then the
+ // projection transform may need to be updated. The projection
+ // policy and clip plane distances and policies may have changed.
+ // The window resize policy and screen scale may have changed,
+ // which affects clip plane distance scaling.
+ updateProjection = true ;
+ updateClipDistances = true ;
+ updatePhysicalToVpScale = true ;
+ updatePhysicalToVirtualScale = true ;
+
+ // View policy, coexistence to tracker base, coexistence centering
+ // enable, or window movement policy may have changed.
+ updateCoeToPlate = true ;
+
+ // Screen scale, resize policy, view policy, view platform,
+ // physical body, physical environment, eyepoint policy, or field
+ // of view may have changed.
+ updateViewPlatformToCoe = true ;
+ updateCoeToViewPlatform = true ;
+ updateCoeToVworld = true ;
+
+ // The image plate to view platform transform is composed from the
+ // coexistence to image plate and view platform to coexistence
+ // transforms, so these need updates.
+ updatePlateToViewPlatform = true ;
+ updatePlateToVworld = true ;
+
+ // View platform to coexistence or coexistence to tracker base may
+ // have changed.
+ updateTrackerBaseToViewPlatform = true ;
+ updateTrackerBaseToVworld = true ;
+
+ // Screen scale policy or explicit screen scale may have changed.
+ updateScreenScale = true ;
+
+ // Update transforms derived from eye info.
+ updateEyeDependencies() ;
+ }
+
+ private void updateHeadDependencies() {
+ if (viewPolicy == View.HMD_VIEW) {
+ // Image plates are fixed relative to the head, so their
+ // positions have changed with respect to coexistence, the
+ // view platform, and the virtual world. The eyes are fixed
+ // with respect to the image plates, so the projection doesn't
+ // change with respect to them.
+ updateCoeToPlate = true ;
+ updatePlateToViewPlatform = true ;
+ updatePlateToVworld = true ;
+ updateViewPlatformToEye = true ;
+ updateEyeToViewPlatform = true ;
+ updateEyeToVworld = true ;
+ updateInverseViewPlatformProjection = true ;
+ updateInverseVworldProjection = true ;
+ }
+ else {
+ // Eye positions have changed with respect to the fixed
+ // screens, so the projections must be updated as well as the
+ // positions.
+ updateEyeInPlate = true ;
+ updateEyeDependencies() ;
+ }
+ }
+
+ private void updateVworldDependencies() {
+ updatePlateToVworld = true ;
+ updateCoeToVworld = true ;
+ updateEyeToVworld = true ;
+ updateTrackerBaseToVworld = true ;
+ updateInverseVworldProjection = true ;
+
+ if (vpi.updateVworldScale)
+ updatePhysicalToVirtualScale = true ;
+
+ if (vpi.updateVworldScale && clipVirtual) {
+ // vworldToViewPlatformScale changed and clip plane distances
+ // are in virtual units.
+ updateProjection = true ;
+ updateClipDistances = true ;
+ updateInverseProjection = true ;
+ updateInverseViewPlatformProjection = true ;
+ }
+ }
+ }
+
+ /**
+ * Prints out the specified transform in a readable format.
+ *
+ * @param t3d transform to be printed
+ * @param name the name of the transform
+ */
+ private static void t3dPrint(Transform3D t3d, String name) {
+ double[] m = new double[16] ;
+ t3d.get(m) ;
+ String[] sa = formatMatrixRows(4, 4, m) ;
+ System.err.println(name) ;
+ for (int i = 0 ; i < 4 ; i++) System.err.println(sa[i]) ;
+ }
+
+ /**
+ * Formats a matrix with fixed fractional digits and integer padding to
+ * align the decimal points in columns. Non-negative numbers print up to
+ * 7 integer digits, while negative numbers print up to 6 integer digits
+ * to account for the negative sign. 6 fractional digits are printed.
+ *
+ * @param rowCount number of rows in the matrix
+ * @param colCount number of columns in the matrix
+ * @param m matrix to be formatted
+ * @return matrix rows formatted into strings
+ */
+ private static String[] formatMatrixRows
+ (int rowCount, int colCount, double[] m) {
+
+ DecimalFormat df = new DecimalFormat("0.000000") ;
+ FieldPosition fp = new FieldPosition(DecimalFormat.INTEGER_FIELD) ;
+ StringBuffer sb0 = new StringBuffer() ;
+ StringBuffer sb1 = new StringBuffer() ;
+ String[] rows = new String[rowCount] ;
+
+ for (int i = 0 ; i < rowCount ; i++) {
+ sb0.setLength(0) ;
+ for (int j = 0 ; j < colCount ; j++) {
+ sb1.setLength(0) ;
+ df.format(m[i*colCount+j], sb1, fp) ;
+ int pad = 8 - fp.getEndIndex() ;
+ for (int k = 0 ; k < pad ; k++) {
+ sb1.insert(0, " ") ;
+ }
+ sb0.append(sb1) ;
+ }
+ rows[i] = sb0.toString() ;
+ }
+ return rows ;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/universe/Viewer.java b/src/classes/share/com/sun/j3d/utils/universe/Viewer.java
new file mode 100644
index 0000000..d1665f6
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/Viewer.java
@@ -0,0 +1,1012 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.universe;
+
+import java.awt.event.*;
+import java.awt.*;
+import java.net.URL;
+import java.util.*;
+import javax.media.j3d.*;
+import javax.swing.*;
+import javax.vecmath.*;
+import com.sun.j3d.audioengines.javasound.JavaSoundMixer;
+import java.applet.*;
+
+/**
+ * The Viewer class holds all the information that describes the physical
+ * and virtual "presence" in the Java 3D universe. The Viewer object
+ * consists of:
+ * <UL>
+ * <LI>Physical Objects</LI>
+ * <UL>
+ * <LI>Canvas3D's - used to render with.</LI>
+ * <LI>PhysicalEnvironment - holds characteristics of the hardware platform
+ * being used to render on.</LI>
+ * <LI>PhysicalBody - holds the physical characteristics and personal
+ * preferences of the person who will be viewing the Java 3D universe.</LI>
+ * </UL>
+ * <LI>Virtual Objects</LI>
+ * <UL>
+ * <LI>View - the Java 3D View object.</LI>
+ * <LI>ViewerAvatar - the geometry that is used by Java 3D to represent the
+ * person viewing the Java 3D universe.</LI>
+ * </UL>
+ * </UL>
+ * If the Viewer object is created without any Canvas3D's, or indirectly
+ * through a configuration file, it will create the Canvas3D's as needed.
+ * The default Viewer creates one Canvas3D. If the Viewer object creates
+ * the Canvas3D's, it will also create a JPanel and JFrame for each Canvas3D.
+ *
+ * Dynamic video resize is a new feature in Java 3D 1.3.1.
+ * This feature provides a means for doing swap synchronous resizing
+ * of the area that is to be magnified (or passed through) to the
+ * output video resolution. This functionality allows an application
+ * to draw into a smaller viewport in the framebuffer in order to reduce
+ * the time spent doing pixel fill. The reduced size viewport is then
+ * magnified up to the video output resolution using the SUN_video_resize
+ * extension. This extension is only implemented in XVR-4000 and later
+ * hardware with back end video out resizing capability.
+ *
+ * If video size compensation is enable, the line widths, point sizes and pixel
+ * operations will be scaled internally with the resize factor to approximately
+ * compensate for video resizing. The location of the pixel ( x, y ) in the
+ * resized framebuffer = ( floor( x * factor + 0.5 ), floor( y * factor + 0.5 ) )
+ *
+ * <p>
+ * @see Canvas3D
+ * @see PhysicalEnvironment
+ * @see PhysicalBody
+ * @see View
+ * @see ViewerAvatar
+ */
+public class Viewer {
+ private static final boolean debug = false;
+ private static PhysicalBody physicalBody = null;
+ private static PhysicalEnvironment physicalEnvironment = null;
+ private View view = null;
+ private ViewerAvatar avatar = null;
+ private Canvas3D[] canvases = null;
+ private JFrame[] j3dJFrames = null;
+ private JPanel[] j3dJPanels = null;
+ private Window[] j3dWindows = null;
+ private ViewingPlatform viewingPlatform = null;
+
+
+ static HashMap viewerMap = new HashMap(5);
+ private float dvrFactor = 1.0f;
+ private boolean doDvr = false;
+ private boolean doDvrResizeCompensation = true;
+
+
+ /**
+ * Get the Viewer associated with the view object.
+ *
+ * @param view The View object for inquiry.
+ * @return The Viewer object associated with this View object.
+ *
+ * Note: This method is targeted for SUN framebuffer XVR-4000 and later
+ * hardware that support video size extension.
+ *
+ * @since Java 3D 1.3.1
+ */
+ // To support a back door for DVR support.
+ public static Viewer getViewer(View view) {
+ Viewer viewer = null;
+ synchronized (viewerMap) {
+ //System.out.println("Viewer.getViewer viewerMap's size is " + viewerMap.size());
+ viewer = (Viewer) (viewerMap.get(view));
+ }
+ return viewer;
+ }
+
+
+ /**
+ * Removes the entry associated with the view object.
+ *
+ * @param view The View object to be removed.
+ * @return The Viewer object associated with this View object.
+ *
+ * Note: This method is targeted for SUN framebuffer XVR-4000 and later
+ * hardware that support video size extension.
+ *
+ * @since Java 3D 1.3.1
+ */
+ // To support a back door for DVR support.
+ public static Viewer removeViewerMapEntry(View view) {
+ Viewer viewer = null;
+ synchronized (viewerMap) {
+
+ viewer = (Viewer) (viewerMap.remove(view));
+ }
+ // System.out.println("viewerMap.size() " + viewerMap.size());
+
+ return viewer;
+ }
+
+
+ /**
+ * Removes all Viewer mappings from the Viewer map.
+ *
+ * Note: This method is targeted for SUN framebuffer XVR-4000 and later
+ * hardware that support video size extension.
+ *
+ * @since Java 3D 1.3.1
+ */
+ // To support a back door for DVR support.
+ public static void clearViewerMap() {
+ synchronized (viewerMap) {
+ viewerMap.clear();
+ }
+ // System.out.println("clearViewerMap - viewerMap.size() " + viewerMap.size());
+
+ }
+
+
+ /**
+ * Returns a status flag indicating whether or not dynamic video size
+ * is enabled.
+ *
+ * Note: This method is targeted for SUN framebuffer XVR-4000 and later
+ * hardware that support video size extension.
+ *
+ * @since Java 3D 1.3.1
+ */
+ // To support a back door for DVR support.
+ public boolean isDvrEnabled() {
+ return doDvr;
+ }
+
+ /**
+ * Turns on or off dynamic video size.
+ *
+ * Note: This method is targeted for SUN framebuffer XVR-4000 and later
+ * hardware that support video size extension.
+ *
+ * @param dvr enables or disables dynamic video size.
+ *
+ * @since Java 3D 1.3.1
+ */
+ // To support a back door for DVR support.
+ public void setDvrEnable(boolean dvr) {
+ doDvr = dvr;
+ view.repaint();
+
+ }
+
+ /**
+ * Retrieves the dynamic video resize factor of this
+ * viewer.
+ *
+ * Note: This method is targeted for SUN framebuffer XVR-4000 and later
+ * hardware that support video size extension.
+ *
+ * @since Java 3D 1.3.1
+ */
+ // To support a back door for DVR support.
+ public float getDvrFactor() {
+ return dvrFactor;
+ }
+
+
+ /**
+ * Set the dynamic video resize factor for this viewer.
+ *
+ * Note: This method is targeted for SUN framebuffer XVR-4000 and later
+ * hardware that support video size extension.
+ *
+ * @param dvr set the dynamic video resize factor for this viewer.
+ *
+ * @since Java 3D 1.3.1
+ */
+ // To support a back door for DVR support.
+ public void setDvrFactor(float dvr) {
+ dvrFactor = dvr;
+ view.repaint();
+
+ }
+
+ /**
+ * Turns on or off dynamic video resize compensation.
+ *
+ * Note: This method is targeted for SUN framebuffer XVR-4000 and later
+ * hardware that support video size extension.
+ *
+ * @param dvrRCE enables or disables dynamic video resize compensation.
+ *
+ * @since Java 3D 1.3.1
+ */
+ // To support a back door for DVR support.
+ public void setDvrResizeCompensationEnable(boolean dvrRCE) {
+ doDvrResizeCompensation = dvrRCE;
+ view.repaint();
+ }
+
+ /**
+ * Returns a status flag indicating whether or not dynamic video resize
+ * compensation is enabled.
+ *
+ * Note: This method is targeted for SUN framebuffer XVR-4000 and later
+ * hardware that support video size extension.
+ *
+ * @since Java 3D 1.3.1
+ */
+ // To support a back door for DVR support.
+ public boolean getDvrResizeCompensationEnable() {
+ return doDvrResizeCompensation;
+ }
+
+ /**
+ * Creates a default viewer object. The default values are used to create
+ * the PhysicalBody and PhysicalEnvironment. A single RGB, double buffered
+ * and depth buffered Canvas3D object is created. The View is created
+ * with a front clip distance of 0.1f and a back clip distance of 10.0f.
+ */
+ public Viewer() {
+ // Call main constructor with default values.
+ this(null, null, null, true);
+ }
+
+ /**
+ * Creates a default viewer object. The default values are used to create
+ * the PhysicalBody and PhysicalEnvironment. The View is created
+ * with a front clip distance of 0.1f and a back clip distance of 10.0f.
+ *
+ * @param userCanvas the Canvas3D object to be used for rendering;
+ * if this is null then a single RGB, double buffered and depth buffered
+ * Canvas3D object is created
+ * @since Java3D 1.1
+ */
+ public Viewer(Canvas3D userCanvas) {
+ // Call main constructor.
+ this(userCanvas == null ? null : new Canvas3D[] {userCanvas},
+ null, null, true);
+ }
+
+
+ /**
+ * Creates a default viewer object. The default values are used to create
+ * the PhysicalBody and PhysicalEnvironment. The View is created
+ * with a front clip distance of 0.1f and a back clip distance of 10.0f.
+ *
+ * @param userCanvases the Canvas3D objects to be used for rendering;
+ * if this is null then a single RGB, double buffered and depth buffered
+ * Canvas3D object is created
+ * @since Java3D 1.3
+ */
+ public Viewer(Canvas3D[] userCanvases) {
+ this(userCanvases, null, null, true);
+ }
+
+ /**
+ * Creates a viewer object. The Canvas3D objects, PhysicalEnvironment, and
+ * PhysicalBody are taken from the arguments.
+ *
+ * @param userCanvases the Canvas3D objects to be used for rendering;
+ * if this is null then a single RGB, double buffered and depth buffered
+ * Canvas3D object is created
+ * @param userBody the PhysicalBody to use for this Viewer; if it is
+ * null, a default PhysicalBody object is created
+ * @param userEnvironment the PhysicalEnvironment to use for this Viewer;
+ * if it is null, a default PhysicalEnvironment object is created
+ * @param setVisible determines if the Frames should be set to visible once created
+ * @since Java3D 1.3
+ */
+ public Viewer(Canvas3D[] userCanvases, PhysicalBody userBody,
+ PhysicalEnvironment userEnvironment, boolean setVisible ) {
+
+ if (userBody == null) {
+ physicalBody = new PhysicalBody();
+ } else {
+ physicalBody = userBody;
+ }
+
+ if (userEnvironment == null) {
+ physicalEnvironment = new PhysicalEnvironment();
+ } else {
+ physicalEnvironment = userEnvironment;
+ }
+
+ // Create Canvas3D object if none was passed in.
+ if (userCanvases == null) {
+ GraphicsConfiguration config =
+ ConfiguredUniverse.getPreferredConfiguration();
+
+ canvases = new Canvas3D[1];
+ canvases[0] = new Canvas3D(config);
+ try {
+ canvases[0].setFocusable( true );
+ } catch(NoSuchMethodError e) {}
+ createFramesAndPanels(setVisible);
+ }
+ else {
+ canvases = new Canvas3D[userCanvases.length];
+ for (int i=0; i<userCanvases.length; i++) {
+ canvases[i] = userCanvases[i];
+ try {
+ canvases[i].setFocusable( true );
+ } catch(NoSuchMethodError e) {}
+ }
+ }
+
+ // Create a View and attach the Canvas3D and the physical
+ // body and environment to the view.
+ view = new View();
+ // Add it to the Viewer's HashMap.
+ synchronized (viewerMap) {
+ Viewer.viewerMap.put(view, this);
+ }
+ for (int i=0; i<canvases.length; i++) {
+ view.addCanvas3D(canvases[i]);
+ }
+ view.setPhysicalBody(physicalBody);
+ view.setPhysicalEnvironment(physicalEnvironment);
+ }
+
+ /**
+ * Creates a default Viewer object. The default values are used to create
+ * the PhysicalEnvironment and PhysicalBody. A single RGB, double buffered
+ * and depth buffered Canvas3D object is created. The View is created
+ * with a front clip distance of 0.1f and a back clip distance of 10.0f.
+ *
+ * @param userConfig the URL of the user configuration file used to
+ * initialize the PhysicalBody object; this is always ignored
+ * @since Java3D 1.1
+ * @deprecated create a ConfiguredUniverse to use a configuration file
+ */
+ public Viewer(URL userConfig) {
+ // Call main constructor.
+ this(null, userConfig);
+ }
+
+ /**
+ * Creates a default viewer object. The default values are used to create
+ * the PhysicalEnvironment and PhysicalBody. The View is created
+ * with a front clip distance of 0.1f and a back clip distance of 10.0f.
+ *
+ * @param userCanvas the Canvas3D object to be used for rendering;
+ * if this is null then a single RGB, double buffered and depth buffered
+ * Canvas3D object is created
+ * @param userConfig the URL of the user configuration file used to
+ * initialize the PhysicalBody object; this is always ignored
+ * @since Java3D 1.1
+ * @deprecated create a ConfiguredUniverse to use a configuration file
+ */
+ public Viewer(Canvas3D userCanvas, URL userConfig) {
+ // Only one PhysicalBody per Universe.
+ if (physicalBody == null) {
+ physicalBody = new PhysicalBody();
+ }
+
+ // Only one PhysicalEnvironment per Universe.
+ if (physicalEnvironment == null) {
+ physicalEnvironment = new PhysicalEnvironment();
+ }
+
+ // Create Canvas3D object if none was passed in.
+ if (userCanvas == null) {
+ GraphicsConfiguration config =
+ SimpleUniverse.getPreferredConfiguration();
+
+ canvases = new Canvas3D[1];
+ canvases[0] = new Canvas3D(config);
+ createFramesAndPanels(true);
+ }
+ else {
+ canvases = new Canvas3D[1];
+ canvases[0] = userCanvas;
+ }
+
+ try {
+ canvases[0].setFocusable( true );
+ } catch(NoSuchMethodError e) {}
+
+ // Create a View and attach the Canvas3D and the physical
+ // body and environment to the view.
+ view = new View();
+ // Add it to the Viewer's HashMap.
+ synchronized (viewerMap) {
+ Viewer.viewerMap.put(view, this);
+ }
+ view.addCanvas3D(canvases[0]);
+ view.setPhysicalBody(physicalBody);
+ view.setPhysicalEnvironment(physicalEnvironment);
+ }
+
+ /**
+ * Package-scoped constructor to create a Viewer from the configuration
+ * objects provided by ConfiguredUniverse.
+ *
+ * @param cs array of ConfigScreen objects containing configuration
+ * information for the physical screens in the environment
+ * @param cv ConfigView object containing configuration information about
+ * the view to be created using the given screens
+ * @param setVisible if true, call setVisible(true) on all created Window
+ * components; otherwise, they remain invisible
+ */
+ Viewer(ConfigScreen[] cs, ConfigView cv, boolean setVisible) {
+
+ // Retrieve the J3D View object from the ConfigView object.
+ // The physical body and environment have already been set there.
+ view = cv.j3dView;
+ // Add it to the Viewer's HashMap.
+ synchronized (viewerMap) {
+ Viewer.viewerMap.put(view, this);
+ }
+
+ // Set this Viewer's references to the physical body and environment.
+ physicalBody = cv.physicalBody;
+ physicalEnvironment = cv.physicalEnvironment;
+
+ // Get available screen devices.
+ //
+ // When running with JDK 1.3.1 or older under the X Window System with
+ // Xinerama enabled, a single screen device is returned which is
+ // actually a virtual screen spanning all the physical screens in the
+ // X display. These can only be configured as a single planar screen
+ // in the configuration file.
+ //
+ // JDK 1.4 and newer returns a screen device for each physical screen,
+ // allowing them to configured as distinct screens with arbitrary
+ // orientations relative to each other.
+ //
+ GraphicsDevice[] devices;
+ GraphicsEnvironment graphicsEnv;
+
+ graphicsEnv = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ devices = graphicsEnv.getScreenDevices();
+
+ if (devices == null)
+ throw new RuntimeException
+ ("\nNo screen devices available in local environment");
+
+ if (debug) {
+ System.out.println
+ ("Viewer: GraphicsEnvironment returned " + devices.length +
+ " GraphicsDevice object" + (devices.length == 1 ? "" : "s"));
+
+ for (int i = 0; i < devices.length; i++) {
+ System.out.println
+ (devices[i] + "\n" +
+ devices[i].getDefaultConfiguration().getBounds() + "\n");
+ }
+ }
+
+ // Allocate the arrays of components to be used. AWT Windows are used
+ // to hold either a JFrame or a JWindow.
+ canvases = new Canvas3D[cs.length];
+ j3dJFrames = new JFrame[cs.length];
+ j3dJPanels = new JPanel[cs.length];
+ j3dWindows = new Window[cs.length];
+
+ // Create a graphics template requesting the desired capabilities.
+ GraphicsConfigTemplate3D tpl3D = new GraphicsConfigTemplate3D();
+ if (cv.stereoEnable) {
+ tpl3D.setStereo(tpl3D.PREFERRED);
+ }
+ if (cv.antialiasingEnable) {
+ tpl3D.setSceneAntialiasing(tpl3D.PREFERRED);
+ }
+
+ // Loop through all screens. Set up the Swing component structure and
+ // the configured attributes for the Canvas3D and Screen3D associated
+ // with each screen.
+ for (int i = 0; i < cs.length; i++) {
+ if (cs[i].frameBufferNumber >= devices.length)
+ throw new ArrayIndexOutOfBoundsException
+ (cs[i].errorMessage
+ (cs[i].creatingCommand,
+ "Screen " + cs[i].frameBufferNumber + " is invalid; " +
+ (devices.length-1) + " is the maximum local index."));
+
+ Rectangle bounds;
+ Container contentPane;
+ GraphicsConfiguration cfg =
+ devices[cs[i].frameBufferNumber].getBestConfiguration(tpl3D);
+
+ if (cfg == null)
+ throw new RuntimeException
+ ("\nNo GraphicsConfiguration on screen " +
+ cs[i].frameBufferNumber + " conforms to template");
+
+ bounds = cfg.getBounds();
+ cs[i].j3dJFrame = j3dJFrames[i] =
+ new JFrame(cs[i].instanceName, cfg);
+
+ if (cs[i].noBorderFullScreen) {
+ try {
+ // Required by JDK 1.4 AWT for borderless full screen.
+ j3dJFrames[i].setUndecorated(true);
+
+ cs[i].j3dWindow = j3dWindows[i] = j3dJFrames[i];
+ contentPane = j3dJFrames[i].getContentPane();
+ }
+ catch (NoSuchMethodError e) {
+ // Handle borderless full screen running under JDK 1.3.1.
+ JWindow jwin = new JWindow(j3dJFrames[i], cfg);
+
+ cs[i].j3dWindow = j3dWindows[i] = jwin;
+ contentPane = jwin.getContentPane();
+ }
+
+ contentPane.setLayout(new BorderLayout());
+ j3dWindows[i].setSize(bounds.width, bounds.height);
+ j3dWindows[i].setLocation(bounds.x, bounds.y);
+ }
+ else {
+ cs[i].j3dWindow = j3dWindows[i] = j3dJFrames[i];
+
+ contentPane = j3dJFrames[i].getContentPane();
+ contentPane.setLayout(new BorderLayout());
+
+ if (cs[i].fullScreen) {
+ j3dWindows[i].setSize(bounds.width, bounds.height);
+ j3dWindows[i].setLocation(bounds.x, bounds.y);
+ }
+ else {
+ j3dWindows[i].setSize(cs[i].windowWidthInPixels,
+ cs[i].windowHeightInPixels);
+ j3dWindows[i].setLocation(bounds.x + cs[i].windowX,
+ bounds.y + cs[i].windowY) ;
+ }
+ }
+
+ // Create a Canvas3D and set its attributes.
+ cs[i].j3dCanvas = canvases[i] = new Canvas3D(cfg);
+ canvases[i].setStereoEnable(cv.stereoEnable);
+ canvases[i].setMonoscopicViewPolicy(cs[i].monoscopicViewPolicy);
+
+ // Get the Screen3D and set its attributes.
+ Screen3D screen = canvases[i].getScreen3D();
+
+ if (cs[i].physicalScreenWidth != 0.0)
+ screen.setPhysicalScreenWidth(cs[i].physicalScreenWidth);
+
+ if (cs[i].physicalScreenHeight != 0.0)
+ screen.setPhysicalScreenHeight(cs[i].physicalScreenHeight);
+
+ if (cs[i].trackerBaseToImagePlate != null)
+ screen.setTrackerBaseToImagePlate
+ (new Transform3D(cs[i].trackerBaseToImagePlate));
+
+ if (cs[i].headTrackerToLeftImagePlate != null)
+ screen.setHeadTrackerToLeftImagePlate
+ (new Transform3D(cs[i].headTrackerToLeftImagePlate));
+
+ if (cs[i].headTrackerToRightImagePlate != null)
+ screen.setHeadTrackerToRightImagePlate
+ (new Transform3D(cs[i].headTrackerToRightImagePlate));
+
+ // Put the Canvas3D into a JPanel.
+ cs[i].j3dJPanel = j3dJPanels[i] = new JPanel();
+ j3dJPanels[i].setLayout(new BorderLayout());
+ j3dJPanels[i].add("Center", canvases[i]);
+
+ // Put the JPanel into the content pane used by JWindow or JFrame.
+ contentPane.add("Center", j3dJPanels[i]);
+
+ // Attach the Canvas3D to the View.
+ view.addCanvas3D(canvases[i]);
+
+ // Add a windowListener to detect the window close event.
+ addWindowCloseListener(j3dWindows[i]);
+
+ // Set Canvas3D focus as required by the JDK 1.4 focus model for
+ // full screen frames. JDK 1.3.1 sets the focus automatically for
+ // full screen components.
+ try {
+ canvases[i].setFocusable(true) ;
+ }
+ catch (NoSuchMethodError e) {
+ }
+
+ if (debug) {
+ System.out.println("Viewer: created Canvas3D for screen " +
+ cs[i].frameBufferNumber + " with size\n " +
+ j3dWindows[i].getSize());
+ System.out.println("Screen3D[" + i + "]: size in pixels (" +
+ screen.getSize().width + " x " +
+ screen.getSize().height + ")");
+ System.out.println(" physical size in meters: (" +
+ screen.getPhysicalScreenWidth() + " x " +
+ screen.getPhysicalScreenHeight() + ")");
+ System.out.println(" hashCode = " + screen.hashCode() + "\n");
+ }
+ }
+
+ if (setVisible)
+ // Call setVisible() on all created Window components.
+ setVisible(true);
+ }
+
+ // Create the JFrames and JPanels for application-supplied Canvas3D
+ // objects.
+ private void createFramesAndPanels( boolean setVisible ) {
+ j3dJFrames = new JFrame[canvases.length];
+ j3dJPanels = new JPanel[canvases.length];
+ j3dWindows = new Window[canvases.length];
+
+ for (int i = 0; i < canvases.length; i++) {
+ j3dWindows[i] = j3dJFrames[i] = new JFrame();
+ j3dJFrames[i].getContentPane().setLayout(new BorderLayout());
+ j3dJFrames[i].setSize(256, 256);
+
+ // Put the Canvas3D into a JPanel.
+ j3dJPanels[i] = new JPanel();
+ j3dJPanels[i].setLayout(new BorderLayout());
+ j3dJPanels[i].add("Center", canvases[i]);
+ j3dJFrames[i].getContentPane().add("Center", j3dJPanels[i]);
+ if (setVisible) {
+ j3dJFrames[i].setVisible(true);
+ }
+ addWindowCloseListener(j3dJFrames[i]);
+ }
+ }
+
+ /**
+ * Call setVisible() on all Window components created by this Viewer.
+ *
+ * @param visible boolean to be passed to the setVisible() calls on the
+ * Window components created by this Viewer
+ * @since Java3D 1.3
+ */
+ public void setVisible(boolean visible) {
+ for (int i = 0; i < j3dWindows.length; i++) {
+ j3dWindows[i].setVisible(visible);
+ }
+ }
+
+ /**
+ * Returns the View object associated with the Viewer object.
+ *
+ * @return The View object of this Viewer.
+ */
+ public View getView() {
+ return view;
+ }
+
+ /**
+ * Set the ViewingPlatform object used by this Viewer.
+ *
+ * @param platform The ViewingPlatform object to set for this
+ * Viewer object. Use null to unset the current value and
+ * not assign assign a new ViewingPlatform object.
+ */
+ public void setViewingPlatform(ViewingPlatform platform) {
+ if (viewingPlatform != null) {
+ viewingPlatform.removeViewer(this);
+ }
+
+ viewingPlatform = platform;
+
+ if (platform != null) {
+ view.attachViewPlatform(platform.getViewPlatform());
+ platform.addViewer(this);
+
+ if (avatar != null)
+ viewingPlatform.setAvatar(this, avatar);
+ }
+ else
+ view.attachViewPlatform(null);
+ }
+ /**
+ * Get the ViewingPlatform object used by this Viewer.
+ *
+ * @return The ViewingPlatform object used by this
+ * Viewer object.
+ */
+ public ViewingPlatform getViewingPlatform() {
+ return viewingPlatform;
+ }
+
+ /**
+ * Sets the geometry to be associated with the viewer's avatar. The
+ * avatar is the geometry used to represent the viewer in the virtual
+ * world.
+ *
+ * @param avatar The geometry to associate with this Viewer object.
+ * Passing in null will cause any geometry associated with the Viewer
+ * to be removed from the scen graph.
+ */
+ public void setAvatar(ViewerAvatar avatar) {
+ // Just return if trying to set the same ViewerAvatar object.
+ if (this.avatar == avatar)
+ return;
+
+ this.avatar = avatar;
+ if (viewingPlatform != null)
+ viewingPlatform.setAvatar(this, this.avatar);
+ }
+
+ /**
+ * Gets the geometry associated with the viewer's avatar. The
+ * avatar is the geometry used to represent the viewer in the virtual
+ * world.
+ *
+ * @return The root of the scene graph that is used to represent the
+ * viewer's avatar.
+ */
+ public ViewerAvatar getAvatar() {
+ return avatar;
+ }
+
+ /**
+ * Returns the PhysicalBody object associated with the Viewer object.
+ *
+ * @return A reference to the PhysicalBody object.
+ */
+ public PhysicalBody getPhysicalBody() {
+ return physicalBody;
+ }
+
+ /**
+ * Returns the PhysicalEnvironment object associated with the Viewer
+ * object.
+ *
+ * @return A reference to the PhysicalEnvironment object.
+ */
+ public PhysicalEnvironment getPhysicalEnvironment() {
+ return physicalEnvironment;
+ }
+
+ /**
+ * Returns the 0th Canvas3D object associated with this Viewer object
+ *
+ * @return a reference to the 0th Canvas3D object associated with this
+ * Viewer object
+ * @since Java3D 1.3
+ */
+ public Canvas3D getCanvas3D() {
+ return canvases[0];
+ }
+
+ /**
+ * Returns the Canvas3D object at the specified index associated with
+ * this Viewer object.
+ *
+ * @param canvasNum the index of the Canvas3D object to retrieve;
+ * if there is no Canvas3D object for the given index, null is returned
+ * @return a reference to a Canvas3D object associated with this
+ * Viewer object
+ * @since Java3D 1.3
+ */
+ public Canvas3D getCanvas3D(int canvasNum) {
+ if (canvasNum > canvases.length) {
+ return null;
+ }
+ return canvases[canvasNum];
+ }
+
+ /**
+ * Returns all the Canvas3D objects associated with this Viewer object.
+ *
+ * @return an array of references to the Canvas3D objects associated with
+ * this Viewer object
+ * @since Java3D 1.3
+ */
+ public Canvas3D[] getCanvas3Ds() {
+ Canvas3D[] ret = new Canvas3D[canvases.length];
+ for (int i = 0; i < canvases.length; i++) {
+ ret[i] = canvases[i];
+ }
+ return ret;
+ }
+
+ /**
+ * Returns the canvas associated with this Viewer object.
+ * @deprecated superceded by getCanvas3D()
+ */
+ public Canvas3D getCanvases() {
+ return getCanvas3D();
+ }
+
+ /**
+ * This method is no longer supported since Java 3D 1.3.
+ * @exception UnsupportedOperationException if called.
+ * @deprecated AWT Frame components are no longer created by the
+ * Viewer class.
+ */
+ public Frame getFrame() {
+ throw new UnsupportedOperationException
+ ("\nAWT Frame components are not created by the Viewer class");
+ }
+
+ /**
+ * Returns the JFrame object created by this Viewer object at the
+ * specified index. If a Viewer is constructed without any Canvas3D
+ * objects then the Viewer object will create a Canva3D object, a JPanel
+ * containing the Canvas3D object, and a JFrame to place the JPanel in.
+ * <p>
+ * NOTE: When running under JDK 1.4 or newer, the JFrame always directly
+ * contains the JPanel which contains the Canvas3D. When running under
+ * JDK 1.3.1 and creating a borderless full screen through a configuration
+ * file, the JFrame will instead contain a JWindow which will contain the
+ * JPanel and Canvas3D.
+ * <p>
+ * @param frameNum the index of the JFrame object to retrieve;
+ * if there is no JFrame object for the given index, null is returned
+ * @return a reference to JFrame object created by this Viewer object
+ * @since Java3D 1.3
+ */
+ public JFrame getJFrame(int frameNum) {
+ if (j3dJFrames == null || frameNum > j3dJFrames.length) {
+ return(null);
+ }
+ return j3dJFrames[frameNum];
+ }
+
+ /**
+ * Returns all the JFrames created by this Viewer object. If a Viewer is
+ * constructed without any Canvas3D objects then the Viewer object will
+ * create a Canva3D object, a JPanel containing the Canvas3D object, and a
+ * JFrame to place the JPanel in.<p>
+ *
+ * NOTE: When running under JDK 1.4 or newer, the JFrame always directly
+ * contains the JPanel which contains the Canvas3D. When running under
+ * JDK 1.3.1 and creating a borderless full screen through a configuration
+ * file, the JFrame will instead contain a JWindow which will contain the
+ * JPanel and Canvas3D.<p>
+ *
+ * @return an array of references to the JFrame objects created by
+ * this Viewer object, or null if no JFrame objects were created
+ * @since Java3D 1.3
+ */
+ public JFrame[] getJFrames() {
+ if (j3dJFrames == null)
+ return null;
+
+ JFrame[] ret = new JFrame[j3dJFrames.length];
+ for (int i = 0; i < j3dJFrames.length; i++) {
+ ret[i] = j3dJFrames[i];
+ }
+ return ret;
+ }
+
+ /**
+ * This method is no longer supported since Java 3D 1.3.
+ * @exception UnsupportedOperationException if called.
+ * @deprecated AWT Panel components are no longer created by the
+ * Viewer class.
+ */
+ public Panel getPanel() {
+ throw new UnsupportedOperationException
+ ("\nAWT Panel components are not created by the Viewer class");
+ }
+
+ /**
+ * Returns the JPanel object created by this Viewer object at the
+ * specified index. If a Viewer is constructed without any Canvas3D
+ * objects then the Viewer object will create a Canva3D object and a
+ * JPanel into which to place the Canvas3D object.
+ *
+ * @param panelNum the index of the JPanel object to retrieve;
+ * if there is no JPanel object for the given index, null is returned
+ * @return a reference to a JPanel object created by this Viewer object
+ * @since Java3D 1.3
+ */
+ public JPanel getJPanel(int panelNum) {
+ if (j3dJPanels == null || panelNum > j3dJPanels.length) {
+ return(null);
+ }
+ return j3dJPanels[panelNum];
+ }
+
+ /**
+ * Returns all the JPanel objects created by this Viewer object. If a
+ * Viewer is constructed without any Canvas3D objects then the Viewer
+ * object will create a Canva3D object and a JPanel into which to place
+ * the Canvas3D object.
+ *
+ * @return an array of references to the JPanel objects created by
+ * this Viewer object, or null or no JPanel objects were created
+ * @since Java3D 1.3
+ */
+ public JPanel[] getJPanels() {
+ if (j3dJPanels == null)
+ return null;
+
+ JPanel[] ret = new JPanel[j3dJPanels.length];
+ for (int i = 0; i < j3dJPanels.length; i++) {
+ ret[i] = j3dJPanels[i];
+ }
+ return ret;
+ }
+
+ /**
+ * Used to create and initialize a default AudioDevice3D used for sound
+ * rendering.
+ *
+ * @return reference to created AudioDevice, or null if error occurs.
+ */
+ public AudioDevice createAudioDevice() {
+ if (physicalEnvironment != null) {
+ AudioDevice3DL2 mixer = new JavaSoundMixer(physicalEnvironment);
+ mixer.initialize();
+ return mixer;
+ }
+ else
+ return null;
+ }
+
+ /**
+ * Returns the Universe to which this Viewer is attached
+ *
+ * @return the Universe to which this Viewer is attached
+ * @since Java 3D 1.3
+ */
+ public SimpleUniverse getUniverse() {
+ return getViewingPlatform().getUniverse();
+ }
+
+
+ /*
+ * Exit if run as an application
+ */
+ void addWindowCloseListener(Window win) {
+ SecurityManager sm = System.getSecurityManager();
+ boolean doExit = true;
+
+ if (sm != null) {
+ try {
+ sm.checkExit(0);
+ } catch (SecurityException e) {
+ doExit = false;
+ }
+ }
+ final boolean _doExit = doExit;
+
+ win.addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent winEvent) {
+ Window w = winEvent.getWindow();
+ w.hide();
+ try {
+ w.dispose();
+ } catch (IllegalStateException e) {}
+ if (_doExit) {
+ System.exit(0);
+ }
+ }
+ });
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/universe/ViewerAvatar.java b/src/classes/share/com/sun/j3d/utils/universe/ViewerAvatar.java
new file mode 100644
index 0000000..e679763
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/ViewerAvatar.java
@@ -0,0 +1,66 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.universe;
+
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+/**
+ * This class holds geomtry that should be associated with the View's
+ * avatar. An avatar is how the user's "virtual self" appears in the
+ * virtual world.
+ *
+ * @see Viewer
+ */
+public class ViewerAvatar extends BranchGroup {
+
+ /**
+ * Constructs an instance of the ViewerAvatar node.
+ */
+ public ViewerAvatar() {
+ setCapability(ALLOW_DETACH);
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/utils/universe/ViewingPlatform.java b/src/classes/share/com/sun/j3d/utils/universe/ViewingPlatform.java
new file mode 100644
index 0000000..ff82848
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/ViewingPlatform.java
@@ -0,0 +1,519 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.utils.universe;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import java.awt.Component;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import com.sun.j3d.utils.behaviors.vp.*;
+import com.sun.j3d.internal.J3dUtilsI18N;
+
+/**
+ * This class is used to set up the "view" side of a Java 3D scene graph.
+ * The ViewingPlatform object contains a MultiTransformGroup node to allow
+ * for a series of transforms to be linked together. To this structure
+ * the ViewPlatform is added as well as any geometry to associate with this
+ * view platform.
+ *
+ * @see ViewPlatform
+ */
+public class ViewingPlatform extends BranchGroup {
+
+ /**
+ * Cached ViewPlatform associated with this ViewingPlatform object.
+ */
+ protected ViewPlatform viewPlatform;
+
+ /**
+ * MultiTransformGroup that holds all TransformGroups between
+ * the BranchGroup and the View object.
+ */
+ protected MultiTransformGroup mtg;
+
+ /**
+ * Used to keep track of added geometry. When geometry
+ * is added to the view platform, an addChild to this BranchGroup
+ * is performed.
+ */
+ protected BranchGroup platformGeometryRoot;
+
+ /**
+ * Used to keep track of added geometry. When geometry
+ * is added for an avatar, an addChild to this BranchGroup
+ * is performed.
+ */
+ protected BranchGroup avatarRoot;
+
+ /**
+ * Cached PlatformGeometry object.
+ */
+ protected PlatformGeometry platformGeometry = null;
+
+ /**
+ * Table of the Viewer objects.
+ */
+ protected Hashtable viewerList;
+
+ /**
+ * Used to keep track of behaviors.
+ *
+ * @since Java 3D 1.2.1
+ */
+ protected BranchGroup behaviors;
+
+ /**
+ * The universe to which this viewing platform is attached
+ *
+ * @since Java 3D 1.3
+ */
+ protected SimpleUniverse universe;
+
+ /**
+ * Creates a default ViewingPlatform object. This consists of a
+ * MultiTransfromGroup node with one transform and a ViewPlatform
+ * object. The ViewPlatform is positioned at (0.0, 0.0, 0.0).
+ */
+ public ViewingPlatform() {
+ // Call main constructor with default values.
+ this(1);
+ }
+
+ /**
+ * Creates the ViewingPlatform object. This consists of a
+ * MultiTransfromGroup node with the specified number of transforms
+ * (all initialized to the identity transform).
+ * and a ViewPlatform object.
+ *
+ * @param numTransforms The number of transforms the MultiTransformGroup
+ * node should contain. If this number is less than 1, 1 is assumed.
+ */
+ public ViewingPlatform(int numTransforms) {
+ viewerList = new Hashtable();
+
+ // Set default capabilities for this node.
+ setCapability(Group.ALLOW_CHILDREN_WRITE);
+ setCapability(Group.ALLOW_CHILDREN_EXTEND);
+ setCapability(BranchGroup.ALLOW_DETACH);
+
+ // Create MultiTransformGroup node.
+ if (numTransforms < 1)
+ numTransforms = 1;
+ mtg = new MultiTransformGroup(numTransforms);
+
+ // Get first transform and add it to the scene graph.
+ TransformGroup tg = mtg.getTransformGroup(0);
+ addChild(tg);
+
+ // Create ViewPlatform and add it to the last transform in the
+ // MultiTransformGroup node.
+ tg = mtg.getTransformGroup(numTransforms - 1);
+ viewPlatform = new ViewPlatform();
+ viewPlatform.setCapability(ViewPlatform.ALLOW_POLICY_READ);
+ viewPlatform.setCapability(ViewPlatform.ALLOW_POLICY_WRITE);
+ tg.addChild(viewPlatform);
+
+ // Set capabilities to allow for changes when live.
+ tg.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
+ tg.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+
+ // Initialize the avatarRoot BranchGroup node and add it to the
+ // last transform in the MultiTransformGroup node.
+ avatarRoot = new BranchGroup();
+ avatarRoot.setCapability(Group.ALLOW_CHILDREN_READ);
+ avatarRoot.setCapability(Group.ALLOW_CHILDREN_WRITE);
+ avatarRoot.setCapability(Group.ALLOW_CHILDREN_EXTEND);
+ tg.addChild(avatarRoot);
+
+ // Initialize the platformGeometry BranchGroup node and add it to the
+ // last transform in the MultiTransformGroup node.
+ platformGeometryRoot = new BranchGroup();
+ platformGeometryRoot.setCapability(Group.ALLOW_CHILDREN_READ);
+ platformGeometryRoot.setCapability(Group.ALLOW_CHILDREN_WRITE);
+ platformGeometryRoot.setCapability(Group.ALLOW_CHILDREN_EXTEND);
+ tg.addChild(platformGeometryRoot);
+ }
+
+ /**
+ * Sets the ViewPlatform node for this ViewingPlatform object.
+ *
+ * @param vp The ViewPlatform node to associate with this ViewingPlatform
+ * object.
+ */
+ public void setViewPlatform(ViewPlatform vp) {
+ TransformGroup tg = getViewPlatformTransform();
+ tg.removeChild(viewPlatform);
+ tg.addChild(vp);
+ viewPlatform = vp;
+ // Assign this to all Viewers.
+ Enumeration e = viewerList.keys();
+
+ while (e.hasMoreElements())
+ ((Viewer)e.nextElement()).setViewingPlatform(this);
+ }
+
+ /**
+ * Returns the ViewPlatform node for this ViewingPlatform object.
+ *
+ * @return The ViewPlatform node associated with this ViewingPlatform
+ * object.
+ */
+ public ViewPlatform getViewPlatform() {
+ return viewPlatform;
+ }
+
+ /**
+ * Assigns the geometry to associate with the ViewingPlatform.
+ * PlatformGeometry is used to hold any geometry to be associated
+ * with the ViewingPlatform. If the ViewingPlatform is to be the
+ * inside of a car, for instance, than the PlatformGeometry could be
+ * the dashboard of the car.
+ *
+ * @param pg The geometry to be associated with this ViewingPlatform.
+ * Passing in null has the effect of deleting any geometry associated
+ * with this ViewingPlatform.
+ */
+ public void setPlatformGeometry(PlatformGeometry pg) {
+ // Just return if trying to set the same PlatformGeometry object.
+ if (platformGeometry == pg)
+ return;
+
+ // If the PlatformGeometry is null, will be removing any geometry
+ // already present.
+ if (pg == null) {
+ if (platformGeometryRoot.numChildren() != 0)
+ platformGeometryRoot.removeChild(0);
+ }
+ else {
+
+ // See if there is an old PlatformGeometry to replace.
+ if (platformGeometryRoot.numChildren() != 0)
+ platformGeometryRoot.setChild(pg, 0);
+ else {
+ platformGeometryRoot.addChild(pg);
+ }
+ }
+ platformGeometry = pg;
+ }
+
+ /**
+ * Returns the PlatformGeometry associated with this ViewingPlatform
+ *
+ * @return The PlatformGeometry associated with this ViewingPlatform
+ */
+ public PlatformGeometry getPlatformGeometry() {
+ return platformGeometry;
+ }
+
+ /**
+ * Returns the MultitransformGroup object for this
+ * ViewingPlatform object.
+ *
+ * @return The MultitransformGroup object.
+ */
+ public MultiTransformGroup getMultiTransformGroup() {
+ return mtg;
+ }
+
+ /**
+ * Returns a reference to the "bottom most" transform in the
+ * MultiTransformGroup that is above the ViewPlatform node.
+ *
+ * @return The TransformGroup that is immediately above the
+ * ViewPlatform object.
+ */
+ public TransformGroup getViewPlatformTransform() {
+ return mtg.getTransformGroup(mtg.getNumTransforms() - 1);
+ }
+
+ /**
+ * Sets the nominal viewing distance in the ViewPlatform transform based
+ * on the current field of view. If the ViewAttachPolicy is not the
+ * default of View.NOMINAL_HEAD, then this method has no effect.<p>
+ *
+ * The ViewPlatform is moved back along Z so that objects at the origin
+ * spanning the normalized X range of -1.0 to +1.0 can be fully viewed
+ * across the width of the window. This is done by setting a translation
+ * of 1/(tan(fieldOfView/2)) in the ViewPlatform transform.<p>
+ *
+ * If there is no Viewer object associated with this ViewingPlatform
+ * object the default field of view of PI/4.0 is used.<p>
+ *
+ * NOTE: Support for multiple Viewer objects is not available. If
+ * multiple viewers are attached to this ViewingPlatform than a
+ * RuntimeException will be thrown.
+ */
+ public void setNominalViewingTransform() {
+ if (viewPlatform.getViewAttachPolicy() == View.NOMINAL_HEAD) {
+ double fieldOfView;
+
+ if (viewerList.size() == 0) {
+ // No Viewer associated with this ViewingPlatform, so use the
+ // default field of view value to move the ViewingPlatform.
+ fieldOfView = Math.PI/4.0;
+ }
+ else {
+ if (viewerList.size() > 1) {
+ throw new RuntimeException
+ (J3dUtilsI18N.getString("ViewingPlatform0"));
+ }
+
+ Viewer viewer = (Viewer)viewerList.keys().nextElement();
+ View view = viewer.getView();
+ fieldOfView = view.getFieldOfView();
+ }
+
+ Transform3D t3d = new Transform3D();
+ double viewDistance = 1.0/Math.tan(fieldOfView/2.0);
+ t3d.set(new Vector3d(0.0, 0.0, viewDistance));
+ getViewPlatformTransform().setTransform(t3d);
+ }
+ }
+
+ /**
+ * Returns the avatarRoot child number of the ViewerAvatar object.
+ * All the children of the avatarRoot are compared with the passed
+ * in ViewerAvatar. If a match is found, the index is returned.
+ *
+ * @param avatar The ViewerAvatar object to look for in the avatarRoot's
+ * child nodes.
+ * @return The index of the child that corresponds to the ViewerAvatar.
+ * If the avatarRoot does not contain the ViewerAvatar -1 is returned.
+ */
+ private int findAvatarChild(ViewerAvatar avatar) {
+ // Search the avatarRoot for the ViewerAvatar associated with
+ // with the Viewer object
+ for (int i = 0; i < avatarRoot.numChildren(); i++) {
+ if (((ViewerAvatar)avatarRoot.getChild(i)) == avatar)
+ return i;
+ }
+
+ // Should never get here.
+ System.err.println("ViewingPlatform.findAvatarChild:Child not found.");
+ return -1;
+ }
+
+ /**
+ * Adds the ViewerAvatar to the scene graph. An avatar (geometry)
+ * can be associated with a Viewer object and displayed by Java 3D.
+ *
+ * @param viewer The viewer object to associate with this avatar.
+ * @param avatar The avatar to add to the scene graph. Passing in
+ * null removes any currently assigned avatar.
+ */
+ void setAvatar(Viewer viewer, ViewerAvatar avatar) {
+ Object oldAvatar = viewerList.get(viewer);
+
+ // A position of -1 means the avatar is not a child of the avatarRoot.
+ int avatarPosition = -1;
+
+ // Because "null" cannot be used in a put the avatarRoot object
+ // is used to signify that there is no ViewerAvatar associated
+ // with this Viewer.
+ if (oldAvatar != avatarRoot)
+ avatarPosition = findAvatarChild((ViewerAvatar)oldAvatar);
+
+ // If the avatar is null, will be removing any geometry already present.
+ if (avatar == null) {
+ if (avatarPosition != -1) {
+ avatarRoot.removeChild(avatarPosition);
+
+ // Reset hashtable entry - avatarRoot == null.
+ viewerList.put(viewer, avatarRoot);
+ }
+ }
+ else {
+ // see if there is an old ViewerAvater to replace
+ if (avatarPosition != -1)
+ avatarRoot.setChild(avatar, avatarPosition);
+ else
+ avatarRoot.addChild(avatar);
+
+ // Update hashtable with new avatar.
+ viewerList.put(viewer, avatar);
+ }
+ }
+
+ /**
+ * When a ViewingPlatform is set by a Viewer, the ViewingPlatform
+ * needs to be informed, via a call to this method. This will add
+ * the Viewer to the ViewingPlatform's viewerList for use when
+ * things such as the PlatformGeometry are changed and all Viewer
+ * scene graphs need to be modified.
+ */
+ void addViewer(Viewer viewer) {
+ // Because the viewerList is also used to associate ViewerAvatars
+ // with Viewer objects a hashtable is used. This routine does not
+ // check for the presence of a ViewerAvatar but the Viewer still
+ // needs to be added to the hashtable. Because "null" cannot be
+ // used in a put the avatarRoot object is used to signify that there
+ // is no ViewerAvatar associated with this Viewer.
+ viewerList.put(viewer, avatarRoot);
+ }
+
+ /*
+ * Cleanup when Viewer set another ViewingPlatform
+ */
+ void removeViewer(Viewer viewer) {
+ viewerList.remove(viewer);
+ }
+
+ /**
+ * Adds a new ViewPlatformBehavior to the ViewingPlatform
+ */
+ void addViewPlatformBehavior(ViewPlatformBehavior behavior) {
+ behavior.setViewingPlatform(this);
+ if (behaviors == null) {
+ behaviors = new BranchGroup();
+ behaviors.setCapability(BranchGroup.ALLOW_DETACH);
+ behaviors.setCapability(BranchGroup.ALLOW_CHILDREN_READ);
+ }
+ // otherwise detach the BranchGroup so we can add to it
+ else {
+ behaviors.detach();
+ }
+ behaviors.addChild(behavior);
+ this.addChild(behaviors);
+ }
+
+ /**
+ * Sets the ViewPlatformBehavior which will operate on the ViewPlatform
+ * transform (the TransformGroup returned by
+ * ViewingPlatform.getViewPlatformTransform()). The ViewPlatformBehavior
+ * may be set after the ViewingPlatform is setLive().
+ * If a behavior is already present, it will be detached and it's
+ * setViewingPlatform method will be called with a parameter of null.
+ * @param behavior The ViewPlatformBehavior to add to the ViewingPlatform.
+ * null will remove the ViewingPlatform behavior.
+ * @since Java 3D 1.2.1
+ */
+ public void setViewPlatformBehavior(ViewPlatformBehavior behavior) {
+ if (behaviors != null) {
+ removeViewPlatformBehavior((ViewPlatformBehavior)behaviors.getChild(0));
+ }
+ if (behavior != null) {
+ addViewPlatformBehavior(behavior);
+ }
+ }
+
+ /**
+ * Removes the specified ViewPlatformBehavior
+ */
+ void removeViewPlatformBehavior(ViewPlatformBehavior behavior) {
+ // remove from the behaviors branch group
+ if (behaviors != null) {
+ behaviors.detach();
+ for (int i = 0; i < behaviors.numChildren(); i++) {
+ if (behaviors.getChild(i) == behavior) {
+ behavior.setViewingPlatform( null );
+ behaviors.removeChild(i);
+ break;
+ }
+ }
+ if (behaviors.numChildren() == 0) behaviors = null;
+ else this.addChild(behaviors);
+ }
+ }
+
+ /**
+ * Returns the number of ViewPlatformBehaviors on the ViewingPlatform
+ */
+ int getViewPlatformBehaviorCount() {
+ return behaviors.numChildren();
+ }
+
+ /**
+ * Returns the ViewPlatformBehavior at the specified index
+ */
+ ViewPlatformBehavior getViewPlatformBehavior(int index) {
+ return (ViewPlatformBehavior)behaviors.getChild(index);
+ }
+
+ /**
+ * Returns the ViewPlatformBehavior
+ * @return the ViewPlatformBehavior for the ViewingPlatform.
+ * Returns null if there is no ViewPlatformBehavior set.
+ * @since Java 3D 1.2.1
+ */
+ public ViewPlatformBehavior getViewPlatformBehavior() {
+ if (behaviors == null) {
+ return null;
+ }
+ return getViewPlatformBehavior(0);
+ }
+
+ /**
+ * Returns the Viewers attached to this ViewingPlatform
+ *
+ * @return the Viewers attached to this viewing platform
+ * @since Java 3D 1.3
+ */
+ public Viewer[] getViewers() {
+ if (viewerList.size() == 0) return null;
+ return (Viewer[])viewerList.keySet().toArray( new Viewer[0] );
+ }
+
+ /**
+ * Returns the Universe to which this ViewingPlatform is attached
+ *
+ * @return the Universe to which this ViewingPlatform is attached
+ * @since Java 3D 1.3
+ */
+ public SimpleUniverse getUniverse() {
+ return universe;
+ }
+
+ /**
+ * Sets the Universe to which this ViewingPlatform is attached
+ *
+ * @param universe the Universe to which this ViewingPlatform is attached
+ * @since Java 3D 1.3
+ */
+ public void setUniverse( SimpleUniverse universe ) {
+ this.universe = universe;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/utils/universe/doc-files/config-examples.html b/src/classes/share/com/sun/j3d/utils/universe/doc-files/config-examples.html
new file mode 100644
index 0000000..8533368
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/doc-files/config-examples.html
@@ -0,0 +1,85 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+
+<!--
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+ -->
+
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="GENERATOR" content="Mozilla/4.76C-CCK-MCD Netscape [en] (X11; U; SunOS 5.8 sun4u) [Netscape]">
+</head>
+<body text="#000000" bgcolor="#FFFFFF" link="#0000EE" vlink="#551A8B" alink="#FF0000">
+&nbsp;
+<h1>
+Example Configuration Files</h1>
+
+<p><br><a href="j3d1x1.html">j3d1x1</a>&nbsp; A single fullscreen
+desktop configuration.
+<p><a href="j3d1x1-behavior.html">j3d1x1-behavior</a>&nbsp; A single
+fullscreen desktop configuration with a configurable view platform behavior.
+<p><a href="j3d1x1-stereo.html">j3d1x1-stereo</a>&nbsp; A single
+fullscreen desktop configuration with stereo viewing.
+<p><a href="j3d1x1-vr.html">j3d1x1-vr</a>&nbsp; A single fullscreen
+desktop configuration with head tracker, stereo viewing, and 6 degree of
+freedom mouse.
+<p><a href="j3d1x1-window.html">j3d1x1-window</a>&nbsp; A single screen
+desktop configuration with a conventional window.
+<p><a href="j3d1x2-flat.html">j3d1x2-flat</a>&nbsp; A dual-screen planar
+desktop configuration.
+<p><a href="j3d1x2-rot30.html">j3d1x2-rot30</a>&nbsp; A dual-screen
+desktop configuration with each screen rotated toward the other by 30 degrees.
+<p><a href="j3d1x3-cave.html">j3d1x3-cave</a>&nbsp; A three-projector cave
+configuration.
+<p><a href="j3d1x3-cave-vr.html">j3d1x3-cave-vr</a>&nbsp; A
+three-projector cave configuration with head tracking and stereo viewing.
+<p><a href="j3d1x3-rot45.html">j3d1x3-rot45</a>&nbsp; A three-screen
+desktop configuration with left and right screens rotated 45 degrees from the
+center screen.
+<p><a href="j3d2x2-flat.html">j3d2x2-flat</a>&nbsp; A four-projector
+configuration arranged in a 2x2 array.
+<br>&nbsp;
+</body>
+</html>
diff --git a/src/classes/share/com/sun/j3d/utils/universe/doc-files/config-syntax.html b/src/classes/share/com/sun/j3d/utils/universe/doc-files/config-syntax.html
new file mode 100644
index 0000000..9198032
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/doc-files/config-syntax.html
@@ -0,0 +1,1973 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+
+<!--
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+ -->
+
+<html>
+<head>
+ <title>The Java 3D Configuration File</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="Author" content="Mark Hood">
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<table border="1" cellpadding="3" cellspacing="0" width="100%">
+<tr bgcolor="#ccccff">
+<td><font size="+2">
+<b>The Java 3D Configuration File</b></font></td>
+</tr>
+</table>
+<br>
+
+<a href="#SyntaxDescription">Syntax Description</a><br>
+<a href="#CommandOverview">Command Overview</a><br>
+<a href="#OverviewOfRelevantViewModelParameters">
+ Overview of Relevant View Model Parameters</a><br>
+<a href="#TopLevelCommandDetails">Top-Level Command Details</a><br>
+<a href="#BuiltInCommandDetails">Built-In Command Details</a><br>
+<a href="#Command_Index">Command Index</a><br>
+<a href="#Property Index">Property Index</a><br>
+
+<p>
+This document is an informal description of the syntax of the Java 3D
+configuration file and a tutorial of the semantics of its various commands.
+Such a file is written by a user or site administrator to describe the physical
+configuration of a local interactive viewing environment. Configuration
+properties that can be described in the file include the sizes, positions, and
+orientations of displays in either fixed screen environments or head mounted
+displays (<i>HMD</i> devices), as well as the input devices and sensors
+available for user interaction apart from the keyboard and mouse abstractions
+provided by the AWT.</p>
+<p>
+A configuration file is used by passing its URL to either a ConfigContainer or
+a ConfiguredUniverse constructor. The method by which a user specifies the
+file is up to the application, but the universe utilities do provide a few
+means to easily enable an application to perform this task. These depend upon
+a Java 3D property, <i>j3d.configURL</i>, that the user can set on the java
+command line with the <i>-D</i> option. Its value should be a URL string
+indicating the location of the desired file. The application can then either
+call the static ConfigContainer methods
+<a href="../ConfigContainer.html#getConfigURL()">getConfigURL</a>
+to retrieve the value of the property, or
+<a href="../ConfigContainer.html#getConfigURL(java.lang.String)">
+getConfigURL(String)</a> to specify a default file to be used in case the
+property is not set. Applications are encouraged to devise their own
+user-friendly mechanisms to retrieve the configuration file, although the
+setting of the <i>j3d.configURL</i> property should be honored if at all
+possible.</p>
+<p>
+If the attempt to open the resource indicated by the URL is successful, then a
+parser will be invoked to read and evaluate the commands it contains and
+deposit the results in the ConfigContainer. The parser will detect syntax
+errors, invalid commands, and bad references, printing descriptive messages to
+System.out, including the line number and text of the offending command. In
+general the parser attempts to continue processing as much of the file as it
+can when encountering an error. Some errors can only be detected after the
+entire file has been evaluated; in those cases an exception will be thrown.</p>
+<p>
+An application may choose to override the settings of the configuration file by
+accessing view-side scenegraph components directly from the ConfigContainer
+once the file is evaluated. Applications should avoid this in general, as most
+settings are physical calibration constants specific to the local interactive
+viewing environment. Nonetheless, application overrides are still sometimes
+appropriate; for example, the application may have knowledge of the contents of
+the scenegraph that enables it to make a better judgement of where the view's
+clipping planes should be.</p>
+<br>
+
+<a name="SyntaxDescription"></a>
+<table border="1" cellpadding="3" cellspacing="0" width="100%">
+<tr bgcolor="#ccccff">
+<td><font size="+2">
+<b>Syntax Description</b></font></td>
+</tr>
+</table>
+<p>
+The configuration file syntax is very simple; scanning any of the
+<a href="config-examples.html">sample configuration files</a> should provide
+the general idea. At the broadest level there are two main types of
+constructs: <i>comments</i> and <i>commands.</i></p>
+<p>
+Comments can be either <i>C </i>or <i>C++ style. </i>In a C-style
+comment all text between successive occurances of /* and */ is ignored.
+A C++ comment begins with // and continues to the end of the line.</p>
+<p>
+A command begins with an opening parenthesis and ends with a closing
+parenthesis. The elements between the parentheses can be of four types:
+alphanumeric strings, numbers, quoted strings, or other commands. During the
+evaluation of a command, any nested command encountered is itself evaluated,
+and the result of that evaluation replaces the nested command within the
+original outer command.</p>
+<p>
+Strings that contain embedded white space, forward slashes, or invalid
+tokens must be enclosed in quotes (either single or double). Common cases are
+URL strings and Unix path names. Numbers are any non-quoted strings that can
+be parsed as double-precision floating point numbers. The strings <i>true,
+True, false,</i> and <i>False</i> are converted to their corresponding boolean
+values. Strings, quoted strings, and numbers are delimited from each other by
+white space.</p>
+<p>
+Commands in the configuration file have four special forms: <i>point,
+matrix, top-level</i>, and <i>built-in</i> commands.</p>
+
+<blockquote>
+<h4>
+Points</h4>
+
+A command that consists entirely of two, three, or four numbers is a 2D point,
+a 3D point, or a 4D point respectively. Any other command that starts with a
+number is a syntax error.
+<p>
+Don't pass 2D, 3D, or 4D points to commands that expect two, three, or four
+numbers instead. This will generate a syntax error indicating an invalid
+number of arguments.</p>
+
+<h4>
+Matrices</h4>
+
+A 3D matrix is a command that consists entirely of three 3D points. A 4D
+matrix consists entirely of either three or four 4D points; if there are only
+three 4D points then the fourth is implicitly considered to be (0.0 0.0 0.0
+1.0). The points define the row elements of each type of matrix. Any other
+command that starts with a point is a syntax error.
+
+<h4>
+Top-level and built-in commands</h4>
+
+All other commands start with an alphanumeric string, the <i>command name</i>
+which identifies it. The remaining elements of the command are its arguments.
+<p>
+Command names can either specify top-level or built-in commands. Top-level
+commands generally configure Java 3D core and utility classes and can only
+appear at the outermost level of parentheses. Built-in commands are provided
+by the parser itself to help construct the arguments to top-level commands.
+Points, matrices, and built-in commands can only be nested within other
+commands.</p>
+<p>
+An error will result if points, matrices, or built-in commands are invoked at
+the outermost level of nesting. Sometimes this error is caused by prematurely
+closing the opening parenthesis of the current top-level command. Such an
+error is usually preceded by an error from the command indicating an invalid
+number of arguments.</p>
+<p>
+Similarly, errors will be generated if top-level commands are nested. Errors
+to this effect are sometimes caused by failing to close all the open
+parentheses of the preceding top-level command.</p>
+</blockquote>
+
+<h4>
+Java property substitution syntax</h4>
+
+All strings are additionally scanned for text enclosed by a starting ${ and a
+matching }. Such text is looked up as a Java system property name and the
+result is substituted back into the string after eliding the starting and
+ending delimiters. For example, the command:
+<p>
+(Include "file:${user.home}/myBody.cfg")</p>
+<p>
+would evaluate the contents of the file "myBody.cfg" in the user's home
+directory on Unix systems. An error is issued if no Java property exists with
+the specified name.</p>
+<p>
+Java property substitution happens early, after tokenization but before command
+evaluation. Substitution can occur any number of times anywhere within any
+quoted or non-quoted string, including command names, property names, and
+property values, as long as the property substitution syntax is not nested.</p>
+<br>
+
+<a name="CommandOverview"></a>
+<table border="1" cellpadding="3" cellspacing="0" width="100%">
+<tr bgcolor="#ccccff">
+<td><font size="+2">
+<b>Command Overview</b></font></td>
+</tr>
+</table>
+<p>
+Most top-level commands configure concrete Java 3D core and utility classes.
+These include PhysicalBody, PhysicalEnvironment, Screen, Sensor, View, and
+ViewPlatform. The Screen, View, and ViewPlatform commands also implicitly
+configure the Canvas3D core class and the Viewer and ViewingPlatform utility
+classes respectively.</p>
+<p>
+These commands come in two forms: the <i>New&lt;class name&gt;</i> and
+<i>&lt;class name&gt;Property</i> commands. All <i>New</i> commands except
+<a href="#NewSensor">NewSensor</a> create new class instances and bind names to
+them, while the <i>Property</i> commands refer to these names and configure
+their corresponding class instances with the parameters specified by the
+command arguments. All references must be to objects previously instantiated
+in the file; forward referencing is not supported. Names must be unique within
+each class.</p>
+<p>
+Implementations of the Java 3D InputDevice interface and concrete subclasses of
+the abstract ViewPlatformBehavior utility class can also be instantiated and
+configured with the same command forms. The <i>New</i> commands for these
+objects accept class names as command arguments and instantiate the objects
+through introspection. Since the details of the implementations are not known,
+configuration parameters are passed through methods which are invoked through
+introspection of the method names specified in the <i>Property</i> command
+arguments.</p>
+<p>
+A method invoked through introspection with a <i>Property</i> command gets its
+arguments as a single array of Objects. Boolean strings get wrapped into
+Boolean objects, and number strings get wrapped into Double. 2D, 3D, and 4D
+points get wrapped into Point2d, Point3d, and Point4d objects respectively,
+while 3D and 4D matrices get wrapped into Matrix3d and Matrix4d
+respectively.</p>
+<br>
+
+<a name="OverviewOfRelevantViewModelParameters"></a>
+<table border="1" cellpadding="3" cellspacing="0" width="100%">
+<tr bgcolor="#ccccff">
+<td><font size="+2">
+<b>Overview of Relevant View Model Parameters</b></font></td>
+</tr>
+</table>
+<p>
+The sample configuration files are annotated to assist users in modifying them
+to suit their particular viewing environments, but it can be helpful to know
+some of the basics of the Java 3D view model that are relevant to writing a
+configuration file. This overview should only be considered an informal
+adjunct to the detailed description in the Java 3D Specification. Reading the
+source code to the <a href="../ViewInfo.html">ViewInfo</a> utility (a public
+implementation of the Java 3D view model) may also be helpful in understanding
+the details of the view model.</p>
+
+<table border="1" cellpadding="3" cellspacing="0" width="100%">
+<tr bgcolor="#eeeeff">
+<td>
+<b>The Camera View Model</b></font></td>
+</tr>
+</table>
+<p>
+In the traditional camera model the camera or eyepoint is positioned and
+oriented with respect to the virtual world via a view transform. Objects
+contained within the field of view are projected onto an image plane, which is
+then mapped onto a display surface, usually a window on a desktop display
+system. While the view direction and camera position can be freely oriented
+and placed anywhere in the virtual world, the projection is accomplished using
+a static frustum defined by the field of view and a flat image plane centered
+about and normal to the view direction.</p>
+<p>
+This model emulates a typical physical camera quite well, but there are many
+viewing configurations that cannot be implemented using image planes that are
+fixed with respect to the view direction. In a multiple screen environment the
+projection frustum for each screen is skewed with respect to the image plane,
+based on the user's nominal viewing position. Realistic stereo views on fixed
+displays use skewed projection frustums derived from the offsets of the two
+eyes from the center of the image plane, while head tracking with fixed
+displays involves dynamically adjusting projection frustums in response to
+varying eye positions relative to the image plane.</p>
+<p>
+In a low-level API such as OpenGL these situations are handled by concatenating
+all defined model and viewing transforms into a single <i>modelview matrix</i>,
+and for a fixed-screen environment explicitly computing the <i>projection
+matrix</i> for each eye, each display surface, and each new head
+position. Every application handling such viewing configurations typically
+reimplements the framework for computing those matrices itself. In these cases
+it would be useful to be able to separate the projection components out of the
+view transform in some representation based on display and eye locations.</p>
+
+<table border="1" cellpadding="3" cellspacing="0" width="100%">
+<tr bgcolor="#eeeeff">
+<td>
+<b>The Java 3D View Model</b></font></td>
+</tr>
+</table>
+<p>
+Based on these requirements, a high-level view model should provide standard
+mechanisms to 1) define a screen's position and orientation in relation to
+other screens in the viewing environment; 2) specify the position of the user's
+eyes or an HMD relative to the physical space in which the screens or nominal
+user position are defined, and to have them updated automatically if tracking
+hardware exists; and 3) describe where the whole physical space is placed and
+oriented in the virtual world. In such a model the appropriate images could
+then be rendered without further computation in application code.</p>
+<p>
+In the Java 3D view model, screen <i>(image plate)</i> positions and
+orientations are defined relative to a fixed frame of reference in the physical
+world, the <i>tracker base</i>, through the
+<a href="#TrackerBaseToImagePlate">TrackerBaseToImagePlate</a> property. The
+tracker base is somewhat abstract in that it is always used to define that
+fixed frame of reference, even if no physical tracking hardware is being
+used. If the <a href="#ViewPolicy">ViewPolicy</a> is HMD_VIEW, then the left
+and right image plates for head mounted displays are defined relative to the
+head tracking sensor, which reports head orientation and position relative to
+the tracker base.</p>
+<p>
+<i>Coexistence coordinates</i> are defined with the
+<a href="#CoexistenceToTrackerBase">CoexistenceToTrackerBase</a> property. It
+provides a frame of reference in the physical world which can be set up by the
+user or application in whatever way is most convenient for positioning and
+orienting screens, physical body attributes, and sensing devices in the virtual
+world. If tracking is enabled, then the eye positions in coexistence are
+computed from the position and orientation of the head tracking sensor and the
+<a href="#HeadToHeadTracker">HeadToHeadTracker</a> matrix; otherwise the eye
+positions in coexistence are set according to the
+<a href="#CenterEyeInCoexistence">CenterEyeInCoexistence</a> property. In HMD
+mode the eye positions are fixed with respect to the image plates.</p>
+<p>
+The mapping of coexistence coordinates into the virtual world is accomplished
+through the <a href="#ViewAttachPolicy">ViewAttachPolicy</a>, which specifies
+the point in coexistence coordinates to which the origin of the <i>view
+platform</i> should be mapped. The view platform is positioned in the virtual
+world by manipulating its <i>view transform</i>, the composite of all the
+transforms from the view platform up to its Locale. The basis vectors (X, Y,
+and Z directions) of the view platform are always aligned with coexistence
+coordinates, and the scaling between view platform coordinates and physical
+coordinates is specified by the
+<a href="#ScreenScalePolicy">ScreenScalePolicy</a>, so this
+establishes the complete mapping of the physical world into the virtual world.
+The projection of the virtual world onto the physical display surfaces is then
+performed automatically by the Java 3D renderer.</p>
+<p>
+By default Java 3D tries to emulate the familiar camera model as much as
+possible to make it easy to run in a conventional windowed desktop display
+environment. It accomplishes this through the following default settings:</p>
+<ul>
+<li>
+<a href="#WindowEyepointPolicy">WindowEyepointPolicy</a> is set to
+<code>RELATIVE_TO_FIELD_OF_VIEW</code>.</li>
+<li>
+<a href="#WindowMovementPolicy">WindowMovementPolicy</a> is set to
+<code>PHYSICAL_WORLD</code>.</li>
+<li>
+<a href="#WindowResizePolicy">WindowResizePolicy</a> is set to
+<code>PHYSICAL_WORLD</code>.</li>
+<li>
+<a href="#ViewAttachPolicy">ViewAttachPolicy</a> is set to
+<code>NOMINAL_HEAD</code>.</li>
+<li>
+<a href="#CoexistenceCenteringEnable">CoexistenceCenteringEnable</a> is set to
+true.</li>
+</ul>
+
+When a configuration file is being used the defaults are oriented towards
+making the setup of multiple screen environments as easy as possible. If the
+coexistence centering enable has not been explicitly set, and either the
+CoexistenceToTrackerBase transform for the view has been set or
+TrackerBaseToImagePlate has been set for any screen, then the following
+defaults are used instead:</p>
+<ul>
+<li>
+<a href="#WindowEyepointPolicy">WindowEyepointPolicy</a> is set to
+<code>RELATIVE_TO_COEXISTENCE</code>.</li>
+<li>
+<a href="#WindowMovementPolicy">WindowMovementPolicy</a> is set to
+<code>VIRTUAL_WORLD</code>.</li>
+<li>
+<a href="#WindowResizePolicy">WindowResizePolicy</a> is set to
+<code>VIRTUAL_WORLD</code>.</li>
+<li>
+<a href="#ViewAttachPolicy">ViewAttachPolicy</a> is set to
+<code>NOMINAL_SCREEN</code>.</li>
+<li>
+<a href="#CoexistenceCenteringEnable">CoexistenceCenteringEnable</a> is set to
+false.</li>
+</ul>
+
+The avove defaults are also used if coexistence centering enable has been
+explictly set false.
+<br><br><br>
+
+<a name="TopLevelCommandDetails"></a>
+<table border="1" cellpadding="3" cellspacing="0" width="100%">
+<tr bgcolor="#ccccff">
+<td><font size="+2">
+<b>Top-Level Command Details</b></font></td>
+</tr>
+</table>
+<p>
+Top-level commands can only appear at the outermost command nesting level.</p>
+<hr>
+
+<h2>
+<a NAME="NewDevice"></a>NewDevice</h2>
+<b>Syntax:</b>
+<br>(NewDevice <i>&lt;instance name&gt; &lt;class name&gt;</i>
+[Alias <i>&lt;alias name&gt;</i>])
+<p>
+This command instantiates the InputDevice implementation specified by the fully
+qualified <i>class name</i> and binds it to <i>instance name</i>. The
+InputDevice is instantiated through introspection of the class name. The
+implementation must provide a parameterless constructor; this can usually be
+done by extending or wrapping available InputDevice implementations.</p>
+<p>
+The last two arguments are optional and define an alias for <i>instance
+name</i>. This is useful in providing a longer descriptive name for the
+application to recognize, or conversely, to provide a shorter alias for a
+longer instance name. Either name may be used to identify the instance.</p>
+<p>
+At the conclusion of configuration file processing all InputDevice
+implementations so defined will be initialized and registered with any
+PhysicalEnvironments that reference them.<p>
+<hr>
+
+<h2>
+<a NAME="DeviceProperty"></a>DeviceProperty</h2>
+<b>Syntax:</b>
+<br>(DeviceProperty <i>&lt;instance name&gt; &lt;method name&gt; &lt;arg0&gt;
+... &lt;argn&gt;</i>)
+<p>
+The details of the InputDevice implementation specified by <i>instance name</i>
+are not known to ConfigContainer, so any parameters to be set through the
+configuration file are passed to the InputDevice instance by invoking <i>method
+name</i> through introspection. The arguments following the method name are
+evaluated and the results passed to the method as an array of Objects.</p>
+<p>
+The required methods can usually be provided by extending or wrapping existing
+InputDevice implementations.</p>
+<hr>
+
+<h2>
+<a NAME="NewSensor"></a>NewSensor</h2>
+<b>Syntax:</b>
+<br>(NewSensor <i>&lt;instance name&gt; &lt;device name&gt;
+&lt;sensor index&gt;</i> [Alias <i>&lt;alias name&gt;</i>])
+<p>
+Retrieves the Sensor at index <i>sensor index</i> in the InputDevice <i>device
+name</i> and binds it to the name specified by <i>instance name</i>. The
+sensor index is a number truncated to its integer value. The InputDevice
+implementation is responsible for creating its own Sensor objects, so this
+command does not create any new instances.</p>
+<p>
+<i>Instance name</i> is used by other commands to reference the indicated
+Sensor. In addition, the name and its associated Sensor instance is made
+available to applications through a Map returned by the ConfigContainer
+method
+<a href="../ConfigContainer.html#getNamedSensors()">getNamedSensors</a>.
+</p>
+<p>
+The last two arguments are optional and define an alias for <i>instance
+name</i>. This is useful in providing a longer descriptive name for the
+application to recognize, or conversely, to provide a shorter alias for a
+longer instance name. Either name may be used to identify the instance.</p>
+<p>
+If the Sensor is to be used for head tracking, or tracking the position and
+orientation of other objects in the physical world, then it must generate 6
+degree of freedom data relative to the tracker base.</p>
+<hr>
+
+<h2>
+<a NAME="SensorProperty"></a>SensorProperty</h2>
+<b>Syntax:</b>
+<br>(SensorProperty <i>&lt;instance name&gt; &lt;property name&gt;
+&lt;property value&gt;</i>)
+<p>
+Sets a Sensor property. The sensor instance is specified by <i>instance
+name</i>, the property to be set by <i>property name</i>, and the value to be
+set by <i>property value</i>. The following sole property may be
+configured:</p>
+
+<blockquote>
+<h4>
+<a NAME="Hotspot"></a>Hotspot</h4>
+A 3D point in the sensor's local coordinate system. The hotspot specifies the
+"active" point which should interact with the virtual world, such as a point
+used for picking or grabbing an object. Its actual interpretation is up to the
+sensor behavior which uses it. Its value is ignored for head tracking sensors.
+</blockquote>
+<hr>
+
+<h2>
+<a NAME="NewScreen"></a>NewScreen<br>
+<a NAME="NewWindow"></a>NewWindow</h2>
+<b>Syntax:</b>
+<br>(NewScreen <i>&lt;instance name&gt; &lt;device index&gt;</i>
+[Alias <i>&lt;alias name&gt;</i>])
+<br>(NewWindow <i>&lt;instance name&gt; &lt;device index&gt;</i>
+[Alias <i>&lt;alias name&gt;</i>])
+<p>
+The above two commands are equivalent. Both create new window resources and
+associate them with the name specified by <i>instance name</i>. The <i>device
+index</i> is a number truncated to its integer value. This integer is the
+index at which the desired AWT GraphicsDevice appears in the array returned by
+the static getScreenDevices() method of GraphicsEnvironment, and specifies the
+physical screen upon which the window should be created. The GraphicsDevice
+order in the array is specific to the local viewing site and display
+system.</p>
+<p>
+The last two arguments are optional and define an alias for <i>instance
+name</i>. This is useful in providing a longer descriptive name for the
+application to recognize, or conversely, to provide a shorter alias for a
+longer instance name. Either name may be used to identify the instance.</p>
+<hr>
+
+<h2>
+<a NAME="ScreenProperty"></a>ScreenProperty<br>
+<a NAME="WindowProperty"></a>WindowProperty</h2>
+<b>Syntax:</b>
+<br>(ScreenProperty <i>&lt;instance name&gt; &lt;property name&gt;
+&lt;property value></i>)
+<br>(WindowProperty <i>&lt;instance name&gt; &lt;property name&gt;
+&lt;property value></i>)
+<p>
+The above two commands are equivalent. The screen or window instance is
+specified by <i>instance name</i>, the property to be set by <i>property
+name</i>, and the value to be set by <i>property value</i>. The following
+properties are configurable:</p>
+
+<blockquote>
+<h4>
+<a NAME="PhysicalScreenWidth"></a>PhysicalScreenWidth</h4>
+A number specifying the screen's image area width in meters. When using a
+configuration file the default is 0.365. For head mounted displays this should
+be the <i>apparent</i> width of the display at the focal plane.
+
+<h4>
+<a NAME="PhysicalScreenHeight"></a>PhysicalScreenHeight</h4>
+A number specifying the screen's image area height in meters. When using a
+configuration file the default is 0.292. For head mounted displays this should
+be the <i>apparent</i> height of the display at the focal plane.
+
+<h4>
+<a NAME="WindowSize"></a>WindowSize</h4>
+This property's value can be a 2D point to create a window with the specified
+X width and Y height in pixels, or it can be either of the strings FullScreen
+or NoBorderFullScreen to specify a full screen canvas with visible frame
+borders or one with no frame borders. A NoBorderFullScreen canvas uses the
+entire physical display surface for its image. The default value is 512 x 512
+pixels. For multiple screen virtual reality installations or head mounted
+displays NoBorderFullScreen should be used.
+
+<h4>
+<a NAME="WindowPosition"></a>WindowPosition</h4>
+This property's value is a 2D point used to create a window with the specified
+X and Y position. These are offsets of the window's upper left corner from the
+screen's upper left corner.
+
+<h4>
+<a NAME="TrackerBaseToImagePlate"></a>TrackerBaseToImagePlate</h4>
+A 4D matrix which transforms points from tracker base coordinates to the image
+plate coordinates for the specified screen. This is only used when a
+ViewPolicy of <code>SCREEN_VIEW</code> is in effect. The matrix value is
+identity by default.
+<p>
+Image plate dimensions are expressed in meters. The origin of the image plate
+is the lower left corner of the screen's image area, with X increasing to the
+right, Y increasing to the top, and Z increasing away from the screen.</p>
+<p>
+The tracker base is somewhat abstract. It is used as a local fixed frame of
+reference for specifying the orientation and position of a screen <i>even when
+tracking hardware is not being used</i>. It is also the frame of reference for
+defining coexistence coordinates with the
+<a href="#CoexistenceToTrackerBase">CoexistenceToTrackerBase</a> matrix.</p>
+<p>
+The <a href="#BuiltInCommandDetails">built-in commands</a> Translate,
+Rotate, RotateTranslate, and TranslateRotate are available to make it easier
+to create transforms of this type.
+
+<h4>
+<a NAME="HeadTrackerToLeftImagePlate"></a>HeadTrackerToLeftImagePlate<br>
+<a NAME="HeadTrackerToRightImagePlate"></a>HeadTrackerToRightImagePlate</h4>
+4D matrices which transform points in the head tracking sensor's local
+coordinate system to a head mounted display's left and right image plates
+respectively. The default value for each is the identity matrix.</p>
+<p>
+These are only used when a ViewPolicy of <code>HMD_VIEW</code> is in effect.
+As with physical screen dimensions, these matrices should indicate the
+<i>apparent</i> location and orientation of the screen images as viewed through
+the head mounted display's optics.</p>
+<p>
+The HMD manufacturer's specifications in terms of angle of view, distance to
+the focal plane, aspect ratio, and percentage of image overlap between the left
+and right views can be used to derive the apparent screen positions with
+respect to the head, and from there the positions with respect to the head
+tracker mounted on the head. In most cases there is 100% overlap between the
+two stereo images, so the matrices for both the left and right screens should
+be identical.</p>
+<p>
+Some HMD devices support field-sequential stereo and are driven as if they were
+a single screen. In that case, only a single screen should be defined, but
+<i>both</i> the left and right head tracker to image plate transforms need to
+be specified for that same screen.</p>
+
+<h4>
+<a NAME="MonoscopicViewPolicy"></a>MonoscopicViewPolicy</h4>
+This property may have the following string values: <code>CYCLOPEAN_EYE_VIEW,
+LEFT_EYE_VIEW</code>, or <code>RIGHT_EYE_VIEW</code>. The default value is
+<code>CYCLOPEAN_EYE_VIEW</code>. This default works for non-stereo displays
+and field-sequential stereo displays where the two stereo images are generated
+on the same canvas.</p>
+<p>
+Some HMD devices can be driven as a single screen if the HMD supports
+field-sequential stereo, so the default policy will work for them as well if a
+stereo view is enabled. If stereo is not enabled, an IllegalStateException will
+be thrown when the ViewPolicy is set to <code>HMD_VIEW</code> with the default
+<code>CYCLOPEAN_EYE_VIEW</code> policy in effect.</p>
+<p>
+The <code>LEFT_EYE_VIEW</code> and <code>RIGHT_EYE_VIEW</code> monoscopic view
+policies are used for generating stereo pairs on separate monoscopic canvases,
+including the left and right canvases needed by HMD devices that are driven by
+two video channels. When using these policies, stereo should <i>not</i> be
+enabled.</p>
+</blockquote>
+<hr>
+
+<h2>
+<a NAME="NewPhysicalEnvironment"></a>NewPhysicalEnvironment</h2>
+<b>Syntax:</b>
+<br>(NewPhysicalEnvironment <i>&lt;instance name&gt;</i>
+[Alias <i>&lt;alias name&gt;</i>])
+<p>
+Creates a new PhysicalEnvironment and binds it to the name given by <i>instance
+name.</i> This specifies the available input devices, which Sensor to use as
+the head tracker, and defines the coexistence coordinate system. Note that
+aside from the head tracker, the configuration file does not provide a way to
+place Sensors into the array maintained by the PhysicalEnvironment. See the
+<a href="../ConfigContainer.html#getNamedSensors()">getNamedSensors</a>
+method of ConfigContainer.</p>
+<p>
+The last two arguments are optional and define an alias for <i>instance
+name</i>. This is useful in providing a longer descriptive name for the
+application to recognize, or conversely, to provide a shorter alias for a
+longer instance name. Either name may be used to identify the instance.</p>
+<hr>
+
+<h2>
+<a NAME="PhysicalEnvironmentProperty"></a>PhysicalEnvironmentProperty</h2>
+<b>Syntax:</b>
+<br>(PhysicalEnvironmentProperty <i>&lt;instance name&gt;
+&lt;property name&gt; &lt;property value&gt;</i>)
+<p>
+Sets a PhysicalEnvironment property. The instance is specified by <i>instance
+name</i>, the property to be set by <i>property name</i>, and the value to be
+set by <i>property value</i>. The following properties are configurable:</p>
+
+<blockquote>
+<h4>
+<a NAME="InputDevice"></a>InputDevice</h4>
+Register an InputDevice implementation instantiated by
+<a href="#NewDevice">NewDevice.</a> The InputDevice instance name is specified
+by the <i>property value</i> string. If an InputDevice is not registered then
+it will not be scheduled to run.
+
+<h4>
+<a NAME="HeadTracker"></a>HeadTracker</h4>
+Register the Sensor which will be used for head tracking. It must provide 6
+degree of freedom position and orientation reads relative to the tracker base.
+The Sensor instance name is specified by the <i>property value</i> string.
+Its corresponding input device must be registered with the InputDevice
+property.
+<p>
+There is no actual method in the core PhysicalEnvironment class to set the head
+tracker. This property is a simplified interface for setting the
+PhysicalEnvironment head index and assigning the head tracking sensor to that
+index. Direct access to the PhysicalEnvironment Sensor array is not supported
+by the configuration file.</p>
+
+<h4>
+<a NAME="CoexistenceToTrackerBase"></a>CoexistenceToTrackerBase</h4>
+A 4D matrix which transforms points in coexistence coordinates to tracker base
+coordinates. This defines the position and orientation of coexistence
+coordinates relative to the tracker base. Its default value is the identity
+matrix, so if it is not set then coexistence coordinates will be the same as
+tracker base coordinates. See
+<a href="#TrackerBaseToImagePlate">TrackerBaseToImagePlate.</a>
+<p>
+The coexistence origin (<i>center of coexistence</i>) positions the physical
+world with respect to the origin of the ViewPlatform in the virtual world.
+This is established through the
+<a href="#ViewAttachPolicy">ViewAttachPolicy</a>, which attaches the view
+platform to either the center of coexistence, the nominal head, or the nominal
+feet. Coexistence coordinates can essentially be thought of as physical
+coordinates, but the real purpose is to define the space in which coordinates
+systems in the physical world - such as tracker base, image plate, and physical
+body - coexist and interact with the virtual world coordinate systems.</p>
+<p>
+The basis vectors (X, Y, and Z directions) of coexistence coordinates are
+always aligned with the basis vectors of the ViewPlatform in the virtual world,
+and the scale factor going from ViewPlatform coordinates to coexistence
+coordinates is set by the <a href="#ScreenScalePolicy">ScreenScalePolicy</a>.
+Together with the ViewPlatform's view attach policy and view transform this
+establishes the complete mapping of the physical world into the virtual
+world.</p>
+<p>
+The positioning and orientation of coexistence coordinates with respect to the
+physical environment is up to the user or application. In a fixed screen
+environment it usually makes most sense to define it in a convenient
+relationship to the primary screen or some intersection of the available
+screens, such that the coexistence origin is in front of and aligned with the
+user's nominal forward gaze direction. This is because the -Z axis of
+coexistence coordinates always points along the view direction defined by the
+view platform's -Z axis.</p>
+<p>
+For example, when using a single screen, the most common mapping puts the
+center of coexistence in the middle of the screen with its basis vectors
+aligned with the screen's image plate coordinate system. With a dual-screen
+system it is usually most convenient to place the center of coexistence in the
+middle of the edge shared by both screens, with its Z axis extending
+perpendicular to the shared edge and maintaining an equal angle to both
+screens. For a 2x2 array of four screens putting the center of coexistence at
+the center of the array is usually a good choice. In a cave configuration
+having the center of coexistence in the middle of the viewing environment can
+facilitate the sense of immersion.</p>
+<p>
+In HMD mode the view attach policy is ignored and is always effectively
+<code>NOMINAL_SCREEN</code>. Coexistence coordinates and view platform
+coordinates are then equivalent except for scale. For HMD configurations
+placing the coexistence coordinate system aligned with some nominal
+front-facing user position works well.</p>
+<p>
+<b>Note:</b> the normal Java 3D default is to place the center of coexistence
+in the middle of the screen or canvas by setting
+<a href="#CoexistenceCenteringEnable">CoexistenceCenteringEnable</a> true by
+default. This only works for a single screen and if both the
+TrackerBaseToImagePlate and CoexistenceToTrackerBase matrices are identity. If
+either of these matrices are set from their default identity values in the
+configuration file, and CoexistenceCenteringEnable has not been set, then the
+centering property will be set false by default.<p>
+</blockquote>
+<hr>
+
+<h2>
+<a NAME="NewPhysicalBody"></a>NewPhysicalBody</h2>
+<b>Syntax:</b>
+<br>(NewPhysicalBody <i>&lt;instance name&gt;</i>
+[Alias <i>&lt;alias name&gt;</i>])
+<p>
+Creates a new PhysicalBody and binds it to the name given by <i>instance
+name</i>. The PhysicalBody is essentiallly the users's head, with the origin
+halfway between the eyes in the plane of the face. Positive X extends to the
+right eye, positive Y up, and positive Z extends into the skull opposite to the
+forward gaze direction. Dimensions are expressed in meters.</p>
+<p>
+The last two arguments are optional and define an alias for <i>instance
+name</i>. This is useful in providing a longer descriptive name for the
+application to recognize, or conversely, to provide a shorter alias for a
+longer instance name. Either name may be used to identify the instance.</p>
+<hr>
+
+<h2>
+<a NAME="PhysicalBodyProperty"></a>PhysicalBodyProperty</h2>
+<b>Syntax:</b>
+<br>(PhysicalBodyProperty <i>&lt;instance name&gt; &lt;property name&gt;
+&lt;property value&gt;</i>)
+<p>
+Sets a PhysicalBody property. The instance is specified by <i>instance
+name</i>, the property to be set by <i>property name</i>, and the value to be
+set by <i>property value</i>. The following properties are configurable:</p>
+
+<blockquote>
+<h4>
+<a NAME="StereoEyeSeparation"></a>StereoEyeSeparation</h4>
+A number indicating the interpupilary distance in meters. This will set the
+left and right eye positions to offsets of half this distance from the head
+origin along its X axis. The default is 0.066 meters.
+<p>
+This property is a simplified interface to setting the PhysicalBody's separate
+left and right eye positions; there is no actual method in PhysicalBody to set
+stereo eye separation, but the results are exactly equivalent.</p>
+
+<h4>
+<a NAME="LeftEarPosition"></a>LeftEarPosition</h4>
+A 3D point which sets the left ear position relative to head coordinates.
+The default is (-0.08, -0.03, 0.09).
+
+<h4>
+<a NAME="RightEarPosition"></a>RightEarPosition</h4>
+A 3D point which sets the right ear position relative to head coordinates.
+The default is (0.08, -0.03, 0.09).
+
+<h4>
+<a NAME="HeadToHeadTracker"></a><b>HeadToHeadTracker</b></h4>
+A 4D matrix which transforms points from head coordinates to the local
+coordinate system of the head tracking sensor. This allows the positions
+of the eyes and ears to be determined from the position and orientation
+of the head tracker. The default is the identity matrix.
+
+<h4>
+<a NAME="NominalEyeOffsetFromNominalScreen"></a>
+NominalEyeOffsetFromNominalScreen</h4>
+A distance in meters used as a calibration parameter for
+<a href="#ViewAttachPolicy">ViewAttachPolicy</a>. <i>It does not actually set
+the position of the eyes</i>. The property is ignored if ViewAttachPolicy is
+NOMINAL_SCREEN. The default value is 0.4572 meters.
+
+<h4>
+<a NAME="NominalEyeHeightFromGround"></a>NominalEyeHeightFromGround</h4>
+A distance in meters used as a calibration parameter for
+<a href="#ViewAttachPolicy">ViewAttachPolicy</a>.
+<i>It does not actually set the position of the eyes</i>. This property is
+ignored if ViewAttachPolicy is not NOMINAL_FEET. The default value is 1.68
+meters.
+</blockquote>
+<hr>
+
+<h2>
+<a NAME="NewView"></a>NewView</h2>
+<b>Syntax:</b>
+<br>(NewView <i>&lt;instance name&gt;</i>
+[Alias <i>&lt;alias name&gt;</i>])
+<p>
+Creates a new view and binds it to the name given by <i>instance name.</i>
+In the configuration file the term <i>view</i> refers to an instance of the
+<a href="../Viewer.html">Viewer</a> utility class, which contains both an
+instance of a core Java 3D View class and an array of Canvas3D instances into
+which to render.</p>
+<p>
+The last two arguments are optional and define an alias for <i>instance
+name</i>. This is useful in providing a longer descriptive name for the
+application to recognize, or conversely, to provide a shorter alias for a
+longer instance name. Either name may be used to identify the instance.</p>
+<p>
+ConfiguredUniverse requires that at least one view be defined. If a view
+platform is not provided, then ConfiguredUniverse will create a default one and
+attach the view to that. If multiple views are defined, then at least one view
+platform must be explicitly provided by the configuration, and the
+ViewPlatform property must be used to attach each view to a view platform.</p>
+<hr>
+
+<h2>
+<a NAME="ViewProperty"></a>ViewProperty</h2>
+Syntax: <br>
+(NewView <i>&lt;instance name&gt; &lt;property name&gt;
+&lt;property value&gt;</i>)
+<p>
+Sets a View property. The view instance is specified by <i>instance name</i>,
+the property to be set by <i>property name</i>, and the value to be set by
+<i>property value</i>. The following properties are configurable:</p>
+
+<blockquote>
+<h4>
+<a NAME="Screen"></a>Screen<br>
+<a NAME="Window"></a>Window</h4>
+These two properties are equivalent. They include a screen created by
+<a href="#NewScreen">NewScreen</a> or a window created by
+<a href="#NewWindow">NewWindow</a> into
+this view. The screen or window name is specified by the <i>property value</i>
+string. Multiple-screen or multiple-window views are created by calling this
+command with each window or screen to be used. If no windows or screens are
+defined for this view then an IllegalArgumentException will be thrown after the
+configuration file has been processed.
+
+<h4>
+<a NAME="PhysicalEnvironment"></a>PhysicalEnvironment</h4>
+Sets the PhysicalEnvironment to be used for this view. The <i>property
+value</i> string specifies the name of a PhysicalEnvironment instance created
+by the <a href="#NewPhysicalEnvironment">NewPhysicalEnvironment</a> command.
+If no PhysicalEnvironment is specified for this view then one with default
+values will be created.
+
+<h4>
+<a NAME="PhysicalBody"></a>PhysicalBody</h4>
+Sets the PhysicalBody to be used for this view. The <i>property
+value</i> string specifies the name of a PhysicalBody instance created
+by the <a href="#NewPhysicalBody">NewPhysicalBody</a> command. If
+no PhysicalBody is specified for this view then one with default values
+will be created.
+
+<h4>
+<a NAME="ViewPlatform"></a>ViewPlatform</h4>
+The <i>property value</i> string is the name of a view platform defined by a
+previous <a href="#NewViewPlatform">NewViewPlatform</a> command. This
+specifies that the view should be attached to the given view platform.
+ConfiguredUniverse requires that a view platform be specified for every defined
+view unless only a single view without a view platform is provided; in that
+case a view platform is created by default and the view is attached to that.
+If one or more view platforms are defined then the view attachments must be
+made explicitly.
+
+<h4>
+<a NAME="ViewPolicy"></a>ViewPolicy</h4>
+The <i>property value</i> string may be either <code>SCREEN_VIEW</code> or
+<code>HMD_VIEW</code> to indicate whether fixed room-mounted screens are being
+used or a head mounted display. The default value is <code>SCREEN_VIEW</code>.
+
+<h4>
+<a NAME="CoexistenceCenteringEnable"></a>CoexistenceCenteringEnable</h4>
+The <i>property value</i> is a boolean string. If true, then the origin of the
+coexistence coordinate system is set to either the middle of the canvas or the
+middle of the screen depending upon whether the WindowMovementPolicy is
+<code>PHYSICAL_WORLD</code> or <code>VIRTUAL_WORLD</code> respectively. The X,
+Y, and Z directions of coexistence coordinates will point in the same
+directions as those of the screen's image plate.
+<p>
+This only works if a single screen is being used and if both the
+<a href="#TrackerBaseToImagePlate">TrackerBaseToImagePlate</a> and
+<a href="#CoexistenceToTrackerBase">CoexistenceToTrackerBase</a> matrices are
+identity. If CoexistenceCenteringEnable is not explicitly set, and either the
+CoexistenceToTrackerBase transform for the view has been set or
+TrackerBaseToImagePlate has been set for any screen, then the centering enable
+will be set to false by default; otherwise, the normal default is true. This
+property is also effectively false whenever the ViewPolicy is set to
+<code>HMD_VIEW</code>.</p>
+
+<h4>
+<a NAME="WindowEyepointPolicy"></a>WindowEyepointPolicy</h4>
+The string value for this property may be either <code>RELATIVE_TO_SCREEN,
+RELATIVE_TO_COEXISTENCE, RELATIVE_TO_WINDOW</code>, or
+<code>RELATIVE_TO_FIELD_OF_VIEW</code>. The normal Java 3D default is
+<code>RELATIVE_TO_FIELD_OF_VIEW</code>. When using a configuration file the
+default is <code>RELATIVE_TO_COEXISTENCE</code> if
+<a href="#CoexistenceCenteringEnable">CoexistenceCenteringEnable</a> is false,
+otherwise the normal default applies. See the
+<a href="../../../../../../javax/media/j3d/View.html#setWindowEyepointPolicy(int)">setWindowEyepointPolicy</a> View method.
+<p>
+For the <code>RELATIVE_TO_SCREEN</code> and <code>RELATIVE_TO_WINDOW</code>
+policies, the eyepoint is set by using the setLeftManualEyeInImagePlate() and
+setRightManualEyeInImagePlate() methods of Canvas3D. The configuration file
+currently does not provide any mechanism for altering these properties from
+their default values. These default values are (0.142, 0.135, 0.4572) for the
+left eye and (0.208, 0.135, 0.4572) for the right eye.</p>
+<p>
+These polices are ignored if head tracking is enabled.</p>
+
+<h4>
+<a NAME="WindowMovementPolicy"></a>WindowMovementPolicy<br>
+<a NAME="WindowResizePolicy"></a>WindowResizePolicy</h4>
+
+The string values for these properties may be either <code>VIRTUAL_WORLD</code>
+or <code>PHYSICAL_WORLD</code>. The normal Java 3D default value for both is
+<code>PHYSICAL_WORLD</code>. When using a configuration file the default
+values are <code>VIRTUAL_WORLD</code> if
+<a href="#CoexistenceCenteringEnable">CoexistenceCenteringEnable</a> is false,
+otherwise the normal defaults apply. See the
+<a href="../../../../../../javax/media/j3d/View.html#setWindowMovementPolicy(int)">setWindowMovementPolicy</a> and
+<a href="../../../../../../javax/media/j3d/View.html#setWindowResizePolicy(int)">setWindowResizePolicy</a> View methods.</p>
+
+<h4>
+<a NAME="CenterEyeInCoexistence"></a>CenterEyeInCoexistence</h4>
+A 3D point which specifies the location of the center eye relative to
+coexistence coordinates. See
+<a href="#CoexistenceToTrackerBase">CoexistenceToTrackerBase</a>. If stereo
+viewing is enabled, then the left and right eye positions are set to offsets
+from this position using the values specified by the PhysicalBody. This
+property is ignored if head tracking is enabled or if WindowEyepointPolicy is
+not <code>RELATIVE_TO_COEXISTENCE</code>. The default value is (0.0, 0.0,
+0.4572).
+<p>
+This property is a simplified interface to setting the View's left
+and right manual eyes in coexistence; there is no actual method in View
+to set a center eye position.</p>
+
+<h4>
+<a NAME="ScreenScalePolicy"></a>ScreenScalePolicy</h4>
+The <i>property value</i> string may be either <code>SCALE_SCREEN_SIZE</code>
+or <code>SCALE_EXPLICIT</code> and determines the source of the <i>screen
+scale</i>, a factor in the scaling of view platform coordinates to physical
+world coordinates.
+<p>
+If the value is <code>SCALE_SCREEN_SIZE</code>, then the screen scale is half
+the physical screen width in meters. If WindowResizePolicy is
+<code>PHYSICAL_WORLD</code>, then this scale is further multiplied by the ratio
+of the window width to the width of the screen (the <i>window scale</i>) to
+produce the scale from view platform coordinates to physical coordinates. This
+allows a virtual world which spans a normalized width of [-1.0 .. 1.0] in view
+platform coordinates to map directly to the physical width of the window or
+screen, depending upon the resize policy.</p>
+<p>
+<code>SCALE_EXPLICIT</code> uses the value of the ScreenScale property as the
+screen scale. It is also further multiplied by the window scale when using the
+<code>PHYSICAL_WORLD</code> window resize policy to produce the scale from view
+platform coordinates to physical coordinates. Viewing configurations
+incorporating multiple screens should generally set the policy to
+<code>SCALE_EXPLICIT</code> and choose a screen scale based on the aggregate
+display size, but the default value of <code>SCALE_SCREEN_SIZE</code> will
+usually work if all the screens are the same size and arranged in a linear
+array.</p>
+<p>
+The view platform is positioned in the virtual world through a chain of
+transforms to the root of the scene graph; this composite transform is its
+localToVWorld transform and must be congruent. If we take the the inverse of
+the scale factor in this transform and call it the <i>view platform scale</i>,
+then the scale from virtual world units to physical world units can be computed
+as the product of this view platform scale, the screen scale, and, when using a
+resize policy of <code>PHYSICAL_WORLD</code>, the window scale. In the usual
+case the view platform scale is 1.0.</p>
+
+<h4>
+<a NAME="ScreenScale"></a>ScreenScale</h4>
+The <i>property value </i>is a number specifying the explicit screen scale
+factor. It is only used if ScreenScalePolicy is <code>SCALE_EXPLICIT</code>;
+otherwise, the screen scale is half the physical width of the screen in meters.
+The default value for this property is 1.0.
+
+<h4>
+<a NAME="BackClipPolicy"></a>BackClipPolicy<br>
+<a NAME="FrontClipPolicy"></a>FrontClipPolicy</h4>
+The string values of these properties may be either <code>PHYSICAL_EYE,
+PHYSICAL_SCREEN, VIRTUAL_EYE</code>, or <code>VIRTUAL_SCREEN</code>. The
+default policies are <code>PHYSICAL_EYE</code>. See the
+<a href="../../../../../../javax/media/j3d/View.html#setFrontClipPolicy(int)">setFrontClipPolicy</a> and
+<a href="../../../../../../javax/media/j3d/View.html#setBackClipPolicy(int)">setBackClipPolicy</a> View methods.
+
+<h4>
+<a NAME="FrontClipDistance"></a>FrontClipDistance<br>
+<a NAME="BackClipDistance"></a>BackClipDistance</h4>
+These property values are numbers. The defaults are 0.1 and 10.0
+respectively. See the
+<a href="../../../../../../javax/media/j3d/View.html#setFrontClipDistance(double)">setFrontClipDistance</a> and
+<a href="../../../../../../javax/media/j3d/View.html#setBackClipDistance(double)">setBackClipDistance</a> View methods.</p>
+<p>
+With the default clip policies of <code>PHYSICAL_EYE</code> the clip distances
+are measured relative to the eye in physical units, so the clip distances
+specified must be scaled to virtual world units in order to determine the
+distances in the virtual world where they would effectively be applied. As
+described in the discussion of
+<a href="#ScreenScalePolicy">ScreenScalePolicy</a> above, the scale from
+virtual units to physical units is the product of the view platform scale
+(usually 1.0), the screen scale, and the window scale (if the window resize
+policy is <code>PHYSICAL_WORLD</code>), so normally the scale from physical
+units to virtual units would be the inverse of that product.</p>
+<p>
+There is a quirk, however, with physical clip plane scaling when the
+<code>PHYSICAL_EYE</code> or <code>PHYSICAL_SCREEN</code> clip policies are
+used with the <code>PHYSICAL_WORLD</code> window resize policy. The locations
+of the clip planes in physical units are not actually set to the physical
+distances as specified, but are in fact <i>scaled by the window scale</i>.
+This means that when determining where the specified physical clip distances
+are in virtual units the scaling to be used is the inverse of the product of
+the screen scale and view platform scale only.</p>
+<p>
+This quirk applies only to scaling physical clip plane distances, and only with
+the <code>PHYSICAL_WORLD</code> resize policy. It was implemented in this
+manner to prevent objects in the virtual world from getting clipped
+unexpectedly when the virtual world scaling changed as the result of a window
+resize. The quirk can be avoided by using the <code>VIRTUAL_EYE</code> or
+<code>VIRTUAL_SCREEN</code> clip policies or by using the
+<code>VIRTUAL_WORLD</code> resize policy, but in most cases the effect is
+benign and doesn't lead to unexpected results.</p>
+
+<h4>
+<a NAME="FieldOfView"></a>FieldOfView</h4>
+This number is the view's horizontal field of view in radians. The default
+value is PI/4. This value is ignored if WindowEyepointPolicy is not
+<code>RELATIVE_TO_FIELD_OF_VIEW</code> or if head tracking is enabled. The
+eyepoint for each canvas associated with the view is set such that it is
+centered in X and Y, with the Z value at the appropriate distance to match the
+specified field of view across the width of the canvas.
+
+<h4>
+<a NAME="StereoEnable"></a>StereoEnable</h4>
+Enable or disable stereo viewing for this view according to the boolean value
+specified by the <i>property value</i> string. The default value is false.
+<p>
+There is no actual method in the core Java 3D View or utility Viewer class to
+enable stereo. A true value for this property causes ConfigContainer to
+attempt to create stereo-capable canvases for all the screens associated with
+this view. Stereo will then be enabled for each canvas successfully created
+with stereo capability.</p>
+
+<h4>
+<a NAME="TrackingEnable"></a>TrackingEnable</h4>
+Enable or disable head tracking for this view according to the boolean value
+specified by the <i>property value</i> string. The default value is false.
+<p>
+Setting this property true causes WindowEyepointPolicy to be ignored; it will
+effectively be <code>RELATIVE_TO_COEXISTENCE</code> with the eyepoint in
+coexistence coordinates computed from reading the head tracking sensor.
+Tracking must be made available by registering an input device and a head
+tracking sensor in PhysicalEnvironment.</p>
+
+<h4>
+<a NAME="AntialiasingEnable"></a>AntialiasingEnable</h4>
+Enable or disable scene antialiasing for this view according to the boolean
+value specified by the <i>property value</i> string. The default value is
+false.
+<p>
+A true value for this property causes ConfigContainer to attempt to create
+a canvas capable of scene antialiasing on each screen associated with this
+view. The scene will then be antialiased on each canvas successfully created
+with that capability.</p>
+<p>
+Line and point antialiasing are independent of scene antialiasing and are
+controlled by the LineAttribute and PointAttribute components of an Appearance.
+If line and point antialiasing is enabled, then they will be antialiased prior
+to scene antialiasing; if scene antialiasing is turned off, then antialiased
+lines and points will still be antialiased.</p>
+</blockquote>
+<hr>
+
+<h2>
+<a NAME="NewViewPlatform"></a>NewViewPlatform</h2>
+<b>Syntax:</b>
+<br>(NewViewPlatform <i>&lt;instance name&gt;</i>
+[Alias <i>&lt;alias name&gt;</i>])
+<p>
+Creates a new view platform and binds it to the name given by <i>instance
+name</i>. In the configuration file the term <i>view platform</i> refers to an
+instance of the <a href="../ViewingPlatform.html">ViewingPlatform</a> utility
+class, which is an extension of BranchGroup containing an instance of a core
+Java 3D ViewPlatform class.</p>
+<p>
+The last two arguments are optional and define an alias for <i>instance
+name</i>. This is useful in providing a longer descriptive name for the
+application to recognize, or conversely, to provide a shorter alias for a
+longer instance name. Either name may be used to identify the instance.</p>
+<hr>
+
+<h2>
+<a NAME="ViewPlatformProperty"></a>ViewPlatformProperty</h2>
+<b>Syntax:</b>
+<br>(ViewPlatformProperty <i>&lt;instance name&gt; &lt;property name&gt;
+&lt;property value&gt;</i>)
+<p>
+Sets a ViewPlatform property. The instance is specified by <i>instance
+name</i>, the property to be set by <i>property name</i>, and the value to be
+set by <i>property value</i>. The following properties are configurable:</p>
+
+<blockquote>
+<h4>
+<a NAME="NominalViewingTransform"></a>NominalViewingTransform</h4>
+The <i>property value</i> is a boolean string indicating whether or not
+the view platform should be backed up in Z to allow objects at the origin to be
+viewed. This only has effect if the ViewAttachPolicy is
+<code>NOMINAL_HEAD</code>. The default value is false. See the
+<a href="../ViewingPlatform.html#setNominalViewingTransform()">setNominalViewingTransform</a>
+method of ViewingPlatform.
+
+<h4>
+<a NAME="InitialViewingTransform"></a>InitialViewingTransform</h4>
+Sets the initial transform of the view platform to the 4D matrix
+specified by <i>property value</i>. The default value is identity.
+
+<h4>
+<a NAME="AllowPolicyRead"></a>AllowPolicyRead</h4>
+The <i>property value</i> is a boolean string indicating whether or not reading
+the ViewAttachPolicy is allowed. The default value is false.
+
+<h4>
+<a NAME="AllowLocalToVworldRead"></a>AllowLocalToVworldRead</h4>
+The <i>property value</i> is a boolean string indicating whether or not reading
+the view platform's localToVworld transform is allowed. The default value is
+false.
+
+<h4>
+<a NAME="ViewPlatformBehavior"></a>ViewPlatformBehavior</h4>
+Attaches a ViewPlatformBehavior instantiated by
+<a href="#NewViewPlatformBehavior">NewViewPlatformBehavior</a>.
+The <i>property value</i> string is the name bound to that instance.
+
+<h4>
+<a NAME="ViewAttachPolicy"></a>ViewAttachPolicy</h4>
+The <i>property value</i> string can be one of <code>NOMINAL_SCREEN,
+NOMINAL_HEAD,</code> or <code>NOMINAL_FEET</code>. This establishes the point
+in coexistence coordinates where the origin of view platform coordinates is
+attached. The basis vectors of view platform coordinates are always aligned
+with those of coexistence coordinates.
+<p>
+For a ViewAttachPolicy of <code>NOMINAL_SCREEN</code>, the ViewPlatform origin
+is set directly to the origin of coexistence, so that ViewPlatform coordinates
+and coexistence coordinates are identical except for scale.</p>
+<p>
+For a ViewAttachPolicy of <code>NOMINAL_HEAD</code>, the ViewPlatform origin is
+set to the origin of the nominal head, the center eye halfway between the left
+and right eyes. The nominal head origin is on the Z axis of coexistence
+coordinates at some offset from the coexistence origin. If the
+WindowEyepointPolicy is <code>RELATIVE_TO_FIELD_OF_VIEW</code>, then this is
+the positive Z offset producing the required field of view relative to the
+width of the canvas at the coexistence origin, tracking the Z offset of the
+eyepoint defined by that policy; otherwise, the Z offset is the
+NominalEyeOffsetFromNominalScreen property of PhysicalBody and is decoupled
+from the actual eyepoint offset.</p>
+<p>
+For a ViewAttachPolicy of <code>NOMINAL_FEET</code>, the ViewPlatform origin is
+at the ground plane, which is NominalEyeHeightFromGround meters along -Y from
+the origin of the nominal head. NominalEyeHeightFromGround is a property of
+PhysicalBody.</p>
+<p>
+<b>Note:</b> The normal Java 3D default is <code>NOMINAL_HEAD</code>. When
+using a configuration file, the default is <code>NOMINAL_HEAD</code> only if
+every view attached to the view platform has a WindowEyepointPolicy of
+<code>RELATIVE_TO_FIELD_OF_VIEW</code>; otherwise, the default is
+<code>NOMINAL_SCREEN</code>. If the view policy is <code>HMD_VIEW</code>, then
+the ViewAttachPolicy is ignored and is always effectively
+<code>NOMINAL_SCREEN</code>.</p>
+</blockquote>
+<hr>
+
+<h2>
+<a NAME="NewViewPlatformBehavior"></a>NewViewPlatformBehavior</h2>
+<b>Syntax:</b>
+<br>(NewViewPlatformBehavior <i>&lt;instance name&gt; &lt;class name&gt;</i>
+[Alias <i>&lt;alias name&gt;</i>])
+<p>
+This command instantiates the concrete subclass of ViewPlatformBehavior
+specified by the fully qualified <i>class name</i> and binds it to <i>instance
+name</i>. The ViewPlatformBehavior is instantiated through introspection of
+the class name. The subclass must provide a parameterless constructor. If no
+such constructor is available, then the behavior must be extended or wrapped to
+provide one and the derived class used instead.</p>
+<p>
+The last two arguments are optional and define an alias for <i>instance
+name</i>. This is useful in providing a longer descriptive name for the
+application to recognize, or conversely, to provide a shorter alias for a
+longer instance name. Either name may be used to identify the instance.</p>
+<p>
+View platform behaviors often need sensors or canvases as event sources to
+drive the behavior action. A subclass of ViewPlatformBehavior always gets the
+current ViewingPlatform through its
+<a href="../../behaviors/vp/ViewPlatformBehavior.html#setViewingPlatform(com.sun.j3d.utils.universe.ViewingPlatform)">setViewingPlatform</a>
+method. When the behavior is initialized, the canvases used by the
+ViewingPlatform can be retrieved by calling its
+<a href="../ViewingPlatform.html#getViewers()">getViewers</a> method and
+then calling each Viewer's
+<a href="../Viewer.html#getCanvas3Ds()">getCanvas3Ds</a> method. Sensors
+can be retrieved by calling the ViewingPlatform method
+<a href="../ViewingPlatform.html#getUniverse()">getUniverse</a>, checking
+to see if the returned SimpleUniverse is a ConfiguredUniverse, and then calling
+its
+<a href="../ConfiguredUniverse.html#getNamedSensors()">getNamedSensors</a>
+method.</p>
+<p>
+Alternatively, the behavior implementation can define its own properties
+and receive canvas and sensor instances directly through the
+<a href="#Canvas3D">Canvas3D</a> and <a href="#Sensor">Sensor</a> built-in
+commands.</p>
+<hr>
+
+<h2>
+<a NAME="ViewPlatformBehaviorProperty"></a>ViewPlatformBehaviorProperty</h2>
+<b>Syntax:</b>
+<br>(ViewPlatformBehaviorProperty <i>&lt;instance name&gt;
+&lt;property name&gt; &lt;property value&gt;</i>)
+<p>
+Sets a property of a ViewPlatformBehavior. The instance is specified by
+<i>instance name</i>, the property to be set by <i>property name</i>, and the
+value to be set by <i>property value</i>. The following properties are
+pre-defined by the abstract ViewPlatformBehavior superclass and may be
+configured directly:<p>
+
+<blockquote>
+<h4>
+<a NAME="SchedulingBounds"></a>SchedulingBounds</h4>
+The scheduling bounds for this behavior. Use the
+<a href="#BoundingSphere">BoundingSphere</a> built-in command to set this
+property. The default is whatever the application or the concrete subclass
+sets.
+
+<h4>
+<a NAME="SchedulingInterval"></a>SchedulingInterval</h4>
+A number indicating the scheduling interval for this behavior. See the
+<a href="../../../../../../javax/media/j3d/Behavior.html#setSchedulingInterval(int)">setSchedulingInterval</a>
+method of Behavior.
+
+<h4>
+<a NAME="HomeTransform"></a>HomeTransform</h4>
+See the ViewPlatformBehavior method
+<a href="../../behaviors/vp/ViewPlatformBehavior.html#setHomeTransform(javax.media.j3d.Transform3D)">setHomeTransform</a>.
+The property value must be a 4D matrix.
+</blockquote>
+<p>
+The details of a concrete subclass of ViewPlatformBehavior are not known to
+ConfigContainer, so any properties specific to the subclass are set by
+using introspection to invoke <i>property name</i> as a method accepting an
+array of Objects as its single parameter. The arguments following the property
+name are evaluated and the results passed to the method through that array of
+Objects. Such methods can usually be provided by extending or wrapping
+existing ViewPlatformBehavior implementations.</p>
+<hr>
+
+<h2>
+<a NAME="NewObject"></a>NewObject</h2>
+<b>Syntax:</b>
+<br>(NewObject <i>&lt;instance name&gt; &lt;class name&gt;</i>
+[Alias <i>&lt;alias name&gt;</i>])
+<p>
+This command instantiates a generic object specified by <i>class
+name</i> and binds it to <i>instance name</i>. The object is instantiated
+through introspection of the class name. The object must provide a
+parameterless constructor; this can usually be done by extending or
+wrapping existing objects.</p>
+<p>
+The last two arguments are optional and define an alias for <i>instance
+name</i>. This is useful in providing a longer descriptive name for the
+application to recognize, or conversely, to provide a shorter alias for a
+longer instance name. Either name may be used to identify the instance.</p>
+<p>
+Objects so defined may be accessed from ConfigContainer through the
+<a href="../ConfigContainer.html#getNamedGenericObjects()">getNamedGenericObjects</a> method.</p>
+<hr>
+
+<h2>
+<a NAME="ObjectProperty"></a>ObjectProperty</h2>
+<b>Syntax:</b>
+<br>(ObjectProperty <i>&lt;instance name&gt; &lt;method name&gt; &lt;arg0&gt;
+... &lt;argn&gt;</i>)
+<p>
+Sets a property of a generic object. The details of the object specified by
+<i>instance name</i> are not known to ConfigContainer, so any parameters to be
+set through the configuration file are passed to the object instance by
+invoking <i>method name</i> through introspection. The arguments following the
+method name are evaluated and the results passed to the method as an array of
+Objects. The required methods can usually be provided by extending or wrapping
+existing objects.</p>
+<hr>
+
+<h2>
+<a NAME="JavaProperty"></a>JavaProperty</h2>
+<b>Syntax:</b>
+<br>(JavaProperty <i>&lt;propertyName&gt;</i> [Default]
+<i>&lt;propertyValue&gt;</i>)
+<p>
+Sets the Java system property <i>propertyName</i> to the string
+<i>propertyValue</i>. If the optional Default keyword is supplied, then the
+property is set only if it doesn't currently have a value.</p>
+<p>
+Java system properties which affect Java 3D are evaluated at the first
+reference to a VirtualUniverse. Setting such properties in the configuration
+file is therefore ineffective if a ConfiguredUniverse constructor which accepts
+a URL directly is used; ConfigContainer must be used instead. Even then, care
+must be taken to avoid static references to VirtualUniverse from objects such
+as Transform3D.</p>
+<p>
+The special Java property substitution syntax ${<i>&lt;propertyName&gt;</i>}
+may be used to access the value of a Java system property anywhere within a
+configuration file.</p>
+<hr>
+
+<h2>
+<a NAME="Include"></a>Include</h2>
+<b>Syntax:</b>
+<br>(Include <i>&lt;URL string&gt;</i>)
+<p>
+Retrieves the configuration file specified by <i>URL string</i> and includes it
+into the current configuration file at the current line. The content of the
+included file is evaluated exactly as if it were pasted into the current file
+at that line. Included files may be arbitrarily nested.</p>
+<p>
+URL strings must be quoted.<p>
+<hr>
+
+<h2>
+<a NAME="Alias"></a>Alias</h2>
+<b>Syntax:</b>
+<br>(<i>&lt;baseName&gt;</i>Alias <i>&lt;aliasName&gt;
+&lt;originalName&gt;</i>)
+<p>
+Creates an alias for the object specified by <i>originalName</i> (which itself
+may be an alias). <i>baseName</i> may be Device, Object, PhysicalBody,
+PhysicalEnvironment, Screen, Window, Sensor, View, ViewPlatform, or
+ViewPlatformBehavior; it specifies the type of the object being aliased.
+Original names and aliases must be unique within a type. Note that there is no
+white space between <i>baseName</i> and Alias.</p>
+<p>
+Aliases are useful for providing shorter or longer descriptive names for an
+original name. This function is also provided by the optional Alias keyword
+available for all <i>New</i> top-level commands. This separate command can be
+used to substitute new names for objects created by generic include files in
+order to conform to the names expected by specific applications.</p>
+<br>
+
+<a name="BuiltInCommandDetails"></a>
+<table border="1" cellpadding="3" cellspacing="0" width="100%">
+<tr bgcolor="#ccccff">
+<td><font size="+2">
+<b>Built-In Command Details</b></font></td>
+</tr>
+</table>
+<p>
+Built-in commands are provided by the parser itself to help construct the
+arguments to top-level commands. They cannot appear at the top level of
+command nesting.</p>
+<p>
+Translate, Rotate, TranslateRotate, and RotateTranslate are useful for
+computing simple affine transforms of the form <i>Source</i>To<i>Target</i>
+(e.g., <i>TrackerBase</i>To<i>ImagePlate</i>). These transform points from the
+<i>Source</i> coordinate system to the <i>Target</i> coordinate system by
+concatenating translation and rotation matrices. Here is a general rule for
+creating such transforms:</p>
+
+<blockquote>
+Subtract (translate) the target origin first if it can be conveniently measured
+or computed relative to the source origin along the source X, Y, and Z basis
+vectors. Then rotate with Euler angles that move the target basis vectors to
+their corresponding source basis vectors.
+<p>
+If instead it is easier to measure or compute the source origin relative to the
+target origin along the target basis vectors, rotate first with Euler angles
+that move the target basis vectors to their corresponding source basis vectors,
+and then add (translate) the source origin.</p>
+</blockquote>
+<p>
+The Canvas3D, Sensor, Device, PhysicalBody, PhysicalEnvironment, View,
+ViewPlatform, ViewPlatformBehavior, and Object built-in commands return
+references to the objects named by their arguments. These are mostly useful
+for InputDevice and ViewPlatformBehavior implementations that define their own
+properties. The return values of these built-in commands should not be passed
+to commands that expect strings.</p>
+<hr>
+
+<h2>
+<a NAME="Translate"></a>Translate</h2>
+<b>Syntax:</b>
+<br>(Translate <i>&lt;x offset&gt; &lt;y offset&gt; &lt;z offset&gt;</i>)
+<p>
+Returns a 4D matrix that translates by the given X, Y, and Z values.</p>
+<hr>
+
+<h2>
+<a NAME="Rotate"></a>Rotate</h2>
+<b>Syntax:</b>
+<br>(Rotate <i>&lt;x degrees&gt; &lt;y degrees&gt; &lt;z degrees&gt;</i>)
+<p>
+Returns a 4D matrix that rotates by the given Euler angles around static X, Y,
+and Z basis vectors: first about X, then Y, and then Z. See the
+<a href="../../../../../../javax/media/j3d/Transform3D.html#setEuler(javax.vecmath.Vector3d)">setEuler</a>
+method of Transform3D.</p>
+<hr>
+
+<h2>
+<a NAME="Concatenate"></a>Concatenate</h2>
+<b>Syntax:</b>
+<br>(Concatenate <i>&lt;m1&gt; &lt;m2&gt;</i>)
+<p>
+Returns a 4D matrix that concatenates 4D matrices <i>m1</i> and <i>m2</i> in
+that order. If a point is transformed by the resulting matrix, then in effect
+the points are first transformed by <i>m1</i> and then <i>m2</i>.</p>
+<hr>
+
+<h2>
+<a NAME="RotateTranslate"></a>RotateTranslate</h2>
+<b>Syntax:</b>
+<br>(RotateTranslate <i>&lt;m1&gt; &lt;m2&gt;</i>)
+<p>
+An alias for the Concatenate command. This is useful to make the
+result of the concatenation explicit.</p>
+<hr>
+
+<h2>
+<a NAME="TranslateRotate"></a>TranslateRotate</h2>
+<b>Syntax:</b>
+<br>(TranslateRotate <i>&lt;m1&gt; &lt;m2&gt;</i>)
+<p>
+An alias for the Concatenate command. This is useful to make the
+result of the concatenation explicit.</p>
+<hr>
+
+<h2>
+<a NAME="BoundingSphere"></a>BoundingSphere</h2>
+<b>Syntax:</b>
+<br>(BoundingSphere <i>&lt;center&gt; &lt;radius&gt;</i>)
+<p>
+Returns a BoundingSphere object using the 3D point <i>center</i> and the given
+<i>radius</i> in meters. <i>radius</i> may be either a number or the string
+Infinite.</p>
+<hr>
+
+<h2>
+<a NAME="Canvas3D"></a>Canvas3D</h2>
+<b>Syntax:</b>
+<br>(Canvas3D <i>&lt;screen or window name&gt;</i>)
+<p>
+Returns the Canvas3D instance specified by the given name. A named Canvas3D is
+created whenever any of the following configuration commands are used:
+
+<blockquote>
+(ViewProperty <i>&lt;view&gt;</i> Screen <i>&lt;screenName&gt;</i>)<br>
+(ViewProperty <i>&lt;view&gt;</i> Window <i>&lt;windowName&gt;</i>)
+</blockquote>
+
+<i>view</i> is the name of a view created with the NewView command. The
+argument to the Canvas3D built-in must be a <i>screenName</i> or
+<i>windowName</i> parameter from one of the above commands.</p>
+<p>
+<b>Note:</b> the NewScreen and NewWindow commands do <i>not</i> create Canvas3D
+instances themselves; they are created only by the above configuration
+commands.</p>
+<hr>
+
+<h2>
+<a NAME="Sensor">Sensor</h2>
+<b>Syntax:</b>
+<br>(Sensor <i>&lt;sensor name&gt;</i>)
+<p>
+Returns the Sensor instance specified by the given name.</p>
+<hr>
+
+<h2>
+<a NAME="Device">Device</h2>
+<b>Syntax:</b>
+<br>(Device <i>&lt;device name&gt;</i>)
+<p>
+Returns the InputDevice instance specified by the given name.</p>
+<hr>
+
+<h2>
+<a NAME="PhysicalBody">PhysicalBody</h2>
+<b>Syntax:</b>
+<br>(PhysicalBody <i>&lt;body name&gt;</i>)
+<p>
+Returns the PhysicalBody instance specified by the given name.</p>
+<hr>
+
+<h2>
+<a NAME="PhysicalEnvironment">PhysicalEnvironment</h2>
+<b>Syntax:</b>
+<br>(PhysicalEnvironment <i>&lt;environment name&gt;</i>)
+<p>
+Returns the PhysicalEnvironment instance specified by the given name.</p>
+<hr>
+
+<h2>
+<a NAME="View">View</h2>
+<b>Syntax:</b>
+<br>(View <i>&lt;view name&gt;</i>)
+<p>
+Returns the Viewer instance specified by the given name.</p>
+<hr>
+
+<h2>
+<a NAME="ViewPlatform">ViewPlatform</h2>
+<b>Syntax:</b>
+<br>(ViewPlatform <i>&lt;view platform name&gt;</i>)
+<p>
+Returns the ViewingPlatform instance specified by the given name.</p>
+<hr>
+
+<h2>
+<a NAME="ViewPlatformBehavior">ViewPlatformBehavior</h2>
+<b>Syntax:</b>
+<br>(ViewPlatformBehavior <i>&lt;behavior name&gt;</i>)
+<p>
+Returns the ViewPlatformBehavior instance specified by the given name.</p>
+<hr>
+
+<h2>
+<a NAME="Object">Object</h2>
+<b>Syntax:</b>
+<br>(Object <i>&lt;generic object name&gt;</i>)
+<p>
+Returns the generic object instance specified by the given name. A generic
+named object is created by the following configuration command:
+
+<blockquote>
+(NewObject <i>&lt;instance name&gt; &lt;class name&gt;</i>
+[Alias <i>&lt;alias name&gt;</i>])
+</blockquote>
+<hr>
+
+<h2>
+<a NAME="ConfigContainer">ConfigContainer</h2>
+<b>Syntax:</b>
+<br>(ConfigContainer)
+<p>
+Returns a reference to the current ConfigContainer.</p>
+<br>
+
+<a name="Command_Index"></a>
+<table border="1" cellpadding="3" cellspacing="0" width="100%">
+<tr bgcolor="#ccccff">
+<td><font size="+2">
+<b>Command Index</b></font></td>
+</tr>
+</table>
+<br>
+
+<table border="1" cellpadding="3" cellspacing="0">
+<tr bgcolor="#eeeeff">
+<th>Command</th>
+<th>Type</th>
+</tr>
+<tr>
+<td><a href="#Alias">Alias</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#BoundingSphere">BoundingSphere</a></td>
+<td>built-in</td>
+</tr>
+<tr>
+<td><a href="#Canvas3D">Canvas3D</a></td>
+<td>built-in</td>
+</tr>
+<tr>
+<td><a href="#Concatenate">Concatenate</a></td>
+<td>built-in</td>
+</tr>
+<tr>
+<td><a href="#ConfigContainer">ConfigContainer</a></td>
+<td>built-in</td>
+</tr>
+<tr>
+<td><a href="#Device">Device</a></td>
+<td>built-in</td>
+</tr>
+<tr>
+<td><a href="#DeviceProperty">DeviceProperty</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#Include">Include</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#JavaProperty">JavaProperty</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#NewDevice">NewDevice</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#NewObject">NewObject</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#NewPhysicalBody">NewPhysicalBody</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#NewPhysicalEnvironment">NewPhysicalEnvironment</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#NewScreen">NewScreen</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#NewSensor">NewSensor</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#NewView">NewView</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#NewViewPlatform">NewViewPlatform</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#NewViewPlatformBehavior">NewViewPlatformBehavior</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#NewWindow">NewWindow</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#Object">Object</a></td>
+<td>built-in</td>
+</tr>
+<tr>
+<td><a href="#ObjectProperty">ObjectProperty</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#PhysicalBody">PhysicalBody</a></td>
+<td>built-in</td>
+</tr>
+<tr>
+<td><a href="#PhysicalBodyProperty">PhysicalBodyProperty</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#PhysicalEnvironment">PhysicalEnvironment</a></td>
+<td>built-in</td>
+</tr>
+<tr>
+<td><a href="#PhysicalEnvironmentProperty">PhysicalEnvironmentProperty</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#Rotate">Rotate</a></td>
+<td>built-in</td>
+</tr>
+<tr>
+<td><a href="#RotateTranslate">RotateTranslate</a></td>
+<td>built-in</td>
+</tr>
+<tr>
+<td><a href="#ScreenProperty">ScreenProperty</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#Sensor">Sensor</a></td>
+<td>built-in</td>
+</tr>
+<tr>
+<td><a href="#SensorProperty">SensorProperty</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#Translate">Translate</a></td>
+<td>built-in</td>
+</tr>
+<tr>
+<td><a href="#TranslateRotate">TranslateRotate</a></td>
+<td>built-in</td>
+</tr>
+<tr>
+<td><a href="#View">View</a></td>
+<td>built-in</td>
+</tr>
+<tr>
+<td><a href="#ViewPlatform">ViewPlatform</a></td>
+<td>built-in</td>
+</tr>
+<tr>
+<td><a href="#ViewPlatformBehavior">ViewPlatformBehavior</a></td>
+<td>built-in</td>
+</tr>
+<tr>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#ViewPlatformProperty">ViewPlatformProperty</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#ViewPlatformBehaviorProperty">ViewPlatformBehaviorProperty</a></td>
+<td>top-level</td>
+</tr>
+<tr>
+<td><a href="#WindowProperty">WindowProperty</a></td>
+<td>top-level</td>
+</tr>
+</table>
+<br><br>
+
+<a name="Property Index"></a>
+<table border="1" cellpadding="3" cellspacing="0" width="100%">
+<tr bgcolor="#ccccff">
+<td><font size="+2">
+<b>Property Index</b></font></td>
+</tr>
+</table>
+<br>
+
+<table border="1" cellpadding="3" cellspacing="0">
+<tr bgcolor="#eeeeff">
+<th>Property</th>
+<th>Command</th>
+</tr>
+<tr>
+<td><a href="#AllowLocalToVworldRead">AllowLocalToVworldRead</a></td>
+<td><a href="#ViewPlatformProperty">ViewPlatformProperty</a></td>
+</tr>
+<tr>
+<td><a href="#AllowPolicyRead">AllowPolicyRead</a></td>
+<td><a href="#ViewPlatformProperty">ViewPlatformProperty</a></td>
+</tr>
+<tr>
+<td><a href="#AntialiasingEnable">AntialiasingEnable</a></td>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+</tr>
+<tr>
+<td><a href="#BackClipDistance">BackClipDistance</a></td>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+</tr>
+<tr>
+<td><a href="#BackClipPolicy">BackClipPolicy</a></td>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+</tr>
+<tr>
+<td><a href="#CenterEyeInCoexistence">CenterEyeInCoexistence</a></td>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+</tr>
+<tr>
+<td><a href="#CoexistenceCenteringEnable">CoexistenceCenteringEnable</a></td>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+</tr>
+<tr>
+<td><a href="#CoexistenceToTrackerBase">CoexistenceToTrackerBase</a></td>
+<td><a href="#PhysicalEnvironmentProperty">PhysicalEnvironmentProperty</a></td>
+</tr>
+<tr>
+<td><a href="#FieldOfView">FieldOfView</a></td>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+</tr>
+<tr>
+<td><a href="#FrontClipDistance">FrontClipDistance</a></td>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+</tr>
+<tr>
+<td><a href="#FrontClipPolicy">FrontClipPolicy</a></td>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+</tr>
+<tr>
+<td><a href="#HeadToHeadTracker">HeadToHeadTracker</a></td>
+<td><a href="#PhysicalBodyProperty">PhysicalBodyProperty</a></td>
+</tr>
+<tr>
+<td><a href="#HeadTracker">HeadTracker</a></td>
+<td><a href="#PhysicalEnvironmentProperty">PhysicalEnvironmentProperty</a></td>
+</tr>
+<tr>
+<td><a href="#HeadTrackerToLeftImagePlate">HeadTrackerToLeftImagePlate</a></td>
+<td><a href="#ScreenProperty">ScreenProperty</a></td>
+</tr>
+<tr>
+<td><a href="#HeadTrackerToRightImagePlate">HeadTrackerToRightImagePlate</a></td>
+<td><a href="#ScreenProperty">ScreenProperty</a></td>
+</tr>
+<tr>
+<td><a href="#HomeTransform">HomeTransform</a></td>
+<td><a href="#ViewPlatformBehaviorProperty">ViewPlatformBehaviorProperty</a></td>
+</tr>
+<tr>
+<td><a href="#Hotspot">Hotspot</a></td>
+<td><a href="#SensorProperty">SensorProperty</a></td>
+</tr>
+<tr>
+<td><a href="#InitialViewingTransform">InitialViewingTransform</a></td>
+<td><a href="#ViewPlatformProperty">ViewPlatformProperty</a></td>
+</tr>
+<tr>
+<td><a href="#InputDevice">InputDevice</a></td>
+<td><a href="#PhysicalEnvironmentProperty">PhysicalEnvironmentProperty</a></td>
+</tr>
+<tr>
+<td><a href="#LeftEarPosition">LeftEarPosition</a></td>
+<td><a href="#PhysicalBodyProperty">PhysicalBodyProperty</a></td>
+</tr>
+<tr>
+<td><a href="#MonoscopicViewPolicy">MonoscopicViewPolicy</a></td>
+<td><a href="#ScreenProperty">ScreenProperty</a></td>
+</tr>
+<tr>
+<td><a href="#NominalEyeHeightFromGround">NominalEyeHeightFromGround</a></td>
+<td><a href="#PhysicalBodyProperty">PhysicalBodyProperty</a></td>
+</tr>
+<tr>
+<td><a href="#NominalEyeOffsetFromNominalScreen">NominalEyeOffsetFromNominalScreen</a></td>
+<td><a href="#PhysicalBodyProperty">PhysicalBodyProperty</a></td>
+</tr>
+<tr>
+<td><a href="#NominalViewingTransform">NominalViewingTransform</a></td>
+<td><a href="#ViewPlatformProperty">ViewPlatformProperty</a></td>
+</tr>
+<tr>
+<td><a href="#PhysicalBody">PhysicalBody</a></td>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+</tr>
+<tr>
+<td><a href="#PhysicalEnvironment">PhysicalEnvironment</a></td>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+</tr>
+<tr>
+<td><a href="#PhysicalScreenHeight">PhysicalScreenHeight</a></td>
+<td><a href="#ScreenProperty">ScreenProperty</a></td>
+</tr>
+<tr>
+<td><a href="#PhysicalScreenWidth">PhysicalScreenWidth</a></td>
+<td><a href="#ScreenProperty">ScreenProperty</a></td>
+</tr>
+<tr>
+<td><a href="#RightEarPosition">RightEarPosition</a></td>
+<td><a href="#PhysicalBodyProperty">PhysicalBodyProperty</a></td>
+</tr>
+<tr>
+<td><a href="#SchedulingBounds">SchedulingBounds</a></td>
+<td><a href="#ViewPlatformBehaviorProperty">ViewPlatformBehaviorProperty</a></td>
+</tr>
+<tr>
+<td><a href="#SchedulingInterval">SchedulingInterval</a></td>
+<td><a href="#ViewPlatformBehaviorProperty">ViewPlatformBehaviorProperty</a></td>
+</tr>
+<tr>
+<td><a href="#Screen">Screen</a></td>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+</tr>
+<tr>
+<td><a href="#ScreenScale">ScreenScale</a></td>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+</tr>
+<tr>
+<td><a href="#ScreenScalePolicy">ScreenScalePolicy</a></td>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+</tr>
+<tr>
+<td><a href="#StereoEnable">StereoEnable</a></td>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+</tr>
+<tr>
+<td><a href="#StereoEyeSeparation">StereoEyeSeparation</a></td>
+<td><a href="#PhysicalBodyProperty">PhysicalBodyProperty</a></td>
+</tr>
+<tr>
+<td><a href="#TrackerBaseToImagePlate">TrackerBaseToImagePlate</a></td>
+<td><a href="#ScreenProperty">ScreenProperty</a></td>
+</tr>
+<tr>
+<td><a href="#TrackingEnable">TrackingEnable</a></td>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+</tr>
+<tr>
+<td><a href="#ViewAttachPolicy">ViewAttachPolicy</a></td>
+<td><a href="#ViewPlatformProperty">ViewPlatformProperty</a></td>
+</tr>
+<tr>
+<td><a href="#ViewPlatform">ViewPlatform</a></td>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+</tr>
+<tr>
+<td><a href="#ViewPlatformBehavior">ViewPlatformBehavior</a></td>
+<td><a href="#ViewPlatformProperty">ViewPlatformProperty</a></td>
+</tr>
+<tr>
+<td><a href="#ViewPolicy">ViewPolicy</a></td>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+</tr>
+<tr>
+<td><a href="#Window">Window</a></td>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+</tr>
+<tr>
+<td><a href="#WindowEyepointPolicy">WindowEyepointPolicy</a></td>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+</tr>
+<tr>
+<td><a href="#WindowMovementPolicy">WindowMovementPolicy</a></td>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+</tr>
+<tr>
+<td><a href="#WindowPosition">WindowPosition</a></td>
+<td><a href="#WindowProperty">WindowProperty</a></td>
+</tr>
+<tr>
+<td><a href="#WindowResizePolicy">WindowResizePolicy</a></td>
+<td><a href="#ViewProperty">ViewProperty</a></td>
+</tr>
+<tr>
+<td><a href="#WindowSize">WindowSize</a></td>
+<td><a href="#WindowProperty">WindowProperty</a></td>
+</tr>
+</table>
+</body>
+</html>
diff --git a/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x1-behavior.html b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x1-behavior.html
new file mode 100644
index 0000000..c27c3a1
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x1-behavior.html
@@ -0,0 +1,109 @@
+<!--
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+ -->
+
+<html>
+<head>
+ <title>j3d1x1-behavior config file</title>
+</head>
+
+<body text="#000000" bgcolor="#FFFFFF" link="#0000EE" vlink="#551A8B" alink="#FF0000">
+<pre>
+/*
+ ************************************************************************
+ *
+ * Java 3D configuration file for single fullscreen desktop configuration.
+ * A view platform behavior is created and configured here as well.
+ *
+ ************************************************************************
+ */
+
+(NewScreen center 0)
+(ScreenProperty center WindowSize NoBorderFullScreen)
+
+(NewView view0)
+(ViewProperty view0 Screen center)
+
+// Create a view platform behavior. Here we use OrbitBehavior, although any
+// concrete subclass of the abstract ViewPlatformBehavior with a parameterless
+// constructor could be used. The logical name to assign to this behavior is
+// specified by the 2nd argument to the NewViewPlatformBehavior command, while
+// the 3rd argument is the name of the ViewPlatformBehavior subclass. It is
+// instantiated through introspection.
+//
+(NewViewPlatformBehavior vpb com.sun.j3d.utils.behaviors.vp.OrbitBehavior)
+
+// Set the scheduling bounds to be a BoundingSphere with its center at
+// (0.0 0.0 0.0) and an infinite radius.
+//
+(ViewPlatformBehaviorProperty vpb SchedulingBounds
+ (BoundingSphere (0.0 0.0 0.0) infinite))
+
+// Set properties specific to OrbitBehavior. All arguments following the
+// method name are wrapped and passed to the specified method as an array of
+// Objects. Strings "true" and "false" get turned into Boolean, and number
+// strings get turned into Double. Constructs such as (0.0 1.0 2.0) and
+// ((0.0 1.0 2.0 0.5) (3.0 4.0 5.0 1.0) (6.0 7.0 8.0 0.0)) get converted to
+// Point3d and Matrix4d respectively. Note that last row of a Matrix4d doesn't
+// need to be specified; it is implicitly (0.0 0.0 0.0 1.0).
+//
+// The REVERSE_ALL flags are usually passed to the OrbitBehavior constructor.
+// Since it is being instantiated with its parameterless constructor the
+// reverse flags are set here explicitly.
+//
+(ViewPlatformBehaviorProperty vpb ReverseTranslate true)
+(ViewPlatformBehaviorProperty vpb ReverseRotate true)
+(ViewPlatformBehaviorProperty vpb ReverseZoom true)
+
+// Create a new view platform and set the view platform behavior.
+//
+(NewViewPlatform vp)
+(ViewPlatformProperty vp ViewPlatformBehavior vpb)
+
+// Attach the view to the view platform.
+(ViewProperty view0 ViewPlatform vp)
+</pre>
+</body>
+</html>
diff --git a/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x1-stereo.html b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x1-stereo.html
new file mode 100644
index 0000000..63bc927
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x1-stereo.html
@@ -0,0 +1,90 @@
+<!--
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+ -->
+
+<html>
+<head>
+ <title>j3d1x1-stereo config file</title>
+</head>
+
+<body text="#000000" bgcolor="#FFFFFF" link="#0000EE" vlink="#551A8B" alink="#FF0000">
+<pre>
+/*
+ ************************************************************************
+ *
+ * Java 3D configuration file for single fullscreen stereo desktop
+ * configuration with no head tracking.
+ *
+ ************************************************************************
+ */
+
+(NewScreen center 0)
+(ScreenProperty center WindowSize NoBorderFullScreen)
+
+// Define the physical body.
+//
+// The head origin is halfway between the eyes, with X extending to the right,
+// Y up, and positive Z extending into the skull.
+//
+(NewPhysicalBody SiteUser)
+
+// Set the interpupilary distance. This sets the LeftEyePosition and
+// RightEyePosition to offsets of half this distance along both directions of
+// the X axis.
+//
+(PhysicalBodyProperty SiteUser StereoEyeSeparation 0.066)
+
+// Create a view using the defined screen and physical body.
+//
+(NewView view0)
+(ViewProperty view0 Screen center)
+(ViewProperty view0 PhysicalBody SiteUser)
+
+// Enable stereo viewing.
+//
+(ViewProperty view0 StereoEnable true)
+</pre>
+</body>
+</html>
diff --git a/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x1-vr.html b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x1-vr.html
new file mode 100644
index 0000000..eb667b5
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x1-vr.html
@@ -0,0 +1,173 @@
+<!--
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+ -->
+
+<html>
+<head>
+ <title>j3d1x1-vr config file</title>
+</head>
+
+<body text="#000000" bgcolor="#FFFFFF" link="#0000EE" vlink="#551A8B" alink="#FF0000">
+<pre>
+/*
+ ************************************************************************
+ *
+ * Java 3D configuration file for a single screen stereo desktop display
+ * using a head tracker and 6DOF mouse.
+ *
+ ************************************************************************
+ */
+
+// Create a screen object and give it a logical name.
+(NewScreen center 0)
+
+// Set the actual available image area.
+(ScreenProperty center PhysicalScreenWidth 0.398)
+(ScreenProperty center PhysicalScreenHeight 0.282)
+(ScreenProperty center WindowSize NoBorderFullScreen)
+
+// Set the TrackerBaseToImagePlate transform for this screen.
+(ScreenProperty center TrackerBaseToImagePlate
+ (RotateTranslate (Rotate 50.000 0.000 0.000)
+ (Translate 0.199 0.376 0.000)))
+
+// Configure the head tracker.
+(NewDevice tracker1 com.sun.j3d.input.LogitechTracker)
+(DeviceProperty tracker1 SerialPort "/dev/ttya")
+(DeviceProperty tracker1 ReceiverBaseline 0.1450)
+(DeviceProperty tracker1 ReceiverLeftLeg 0.0875)
+(DeviceProperty tracker1 ReceiverHeight 0.0470)
+(DeviceProperty tracker1 ReceiverTopOffset 0.0000)
+(DeviceProperty tracker1 RealtimeSerialBuffer true)
+
+// Configure the 6DOF wand.
+(NewDevice tracker2 com.sun.j3d.input.LogitechTracker)
+(DeviceProperty tracker2 SerialPort "/dev/ttyb")
+(DeviceProperty tracker2 ReceiverBaseline 0.0700)
+(DeviceProperty tracker2 ReceiverLeftLeg 0.0625)
+(DeviceProperty tracker2 ReceiverHeight 0.0510)
+(DeviceProperty tracker2 ReceiverTopOffset 0.0000)
+(DeviceProperty tracker2 RealtimeSerialBuffer true)
+
+// Make the tracker2 device a slave of the tracker1 device.
+(DeviceProperty tracker1 Slave (Device tracker2))
+
+// Create a 2D mouse valuator.
+(NewDevice mouse com.sun.j3d.input.Mouse2DValuator)
+(DeviceProperty mouse Components (Canvas3D center))
+
+// Create logical names for the available sensors.
+(NewSensor head tracker1 0)
+(NewSensor mouse6d tracker2 0)
+(NewSensor mouse2d mouse 0)
+
+// Set the 6DOF mouse sensor hotspot in the local sensor coordinate system.
+(SensorProperty mouse6d Hotspot (0.00 0.00 -0.10))
+
+// Create a physical environment.
+(NewPhysicalEnvironment SampleSite)
+
+// Register the input devices and head tracker sensor.
+(PhysicalEnvironmentProperty SampleSite InputDevice tracker1)
+(PhysicalEnvironmentProperty SampleSite InputDevice tracker2)
+(PhysicalEnvironmentProperty SampleSite InputDevice mouse)
+(PhysicalEnvironmentProperty SampleSite HeadTracker head)
+
+// Define coexistence coordinates.
+(PhysicalEnvironmentProperty SampleSite CoexistenceToTrackerBase
+ (TranslateRotate (Translate 0.0 -0.235 0.0)
+ (Rotate -50.0 0.0 0.0)))
+
+// Define the physical body.
+(NewPhysicalBody SiteUser)
+
+// Set the interpupilary distance.
+(PhysicalBodyProperty SiteUser StereoEyeSeparation 0.066)
+
+// Define the head location relative to the tracker mounted on the head.
+(PhysicalBodyProperty SiteUser HeadToHeadTracker ((1.0 0.0 0.0 0.000)
+ (0.0 1.0 0.0 0.020)
+ (0.0 0.0 1.0 0.018)))
+
+// Create a view platform behavior.
+//
+(NewViewPlatformBehavior vpb com.sun.j3d.utils.behaviors.vp.WandViewBehavior)
+
+(ViewPlatformBehaviorProperty vpb Sensor6D (Sensor mouse6d))
+(ViewPlatformBehaviorProperty vpb Sensor2D (Sensor mouse2d))
+
+(ViewPlatformBehaviorProperty vpb ButtonAction6D 1 GrabView)
+(ViewPlatformBehaviorProperty vpb ButtonAction6D 2 TranslateForward)
+(ViewPlatformBehaviorProperty vpb ButtonAction6D 0 TranslateBackward)
+
+(ViewPlatformBehaviorProperty vpb RotationCoords ViewPlatform)
+(ViewPlatformBehaviorProperty vpb ButtonAction2D 1 Translation)
+(ViewPlatformBehaviorProperty vpb ButtonAction2D 2 Scale)
+
+(ViewPlatformBehaviorProperty vpb EchoType Beam)
+(ViewPlatformBehaviorProperty vpb EchoSize 0.004)
+
+(ViewPlatformBehaviorProperty vpb EchoColor 1.0 0.7 0.0)
+(ViewPlatformBehaviorProperty vpb EchoTransparency 0.4)
+
+// Create a new view platform and set the view platform behavior.
+//
+(NewViewPlatform vp)
+(ViewPlatformProperty vp ViewPlatformBehavior vpb)
+
+// Create a view.
+//
+(NewView view0)
+(ViewProperty view0 Screen center)
+(ViewProperty view0 PhysicalEnvironment SampleSite)
+(ViewProperty view0 PhysicalBody SiteUser)
+(ViewProperty view0 ViewPlatform vp)
+
+// Enable stereo viewing and head tracking.
+(ViewProperty view0 StereoEnable true)
+(ViewProperty view0 TrackingEnable True)
+</pre>
+</body>
+</html>
diff --git a/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x1-window.html b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x1-window.html
new file mode 100644
index 0000000..96da173
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x1-window.html
@@ -0,0 +1,70 @@
+<!--
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+ -->
+
+<html>
+<head>
+ <title>j3d1x1-window config file</title>
+</head>
+
+<body text="#000000" bgcolor="#FFFFFF" link="#0000EE" vlink="#551A8B" alink="#FF0000">
+<pre>
+/*
+ ************************************************************************
+ *
+ * Java 3D configuration file for a conventional single screen, windowed
+ * desktop configuration.
+ *
+ ************************************************************************
+ */
+
+(NewWindow window1 0)
+(WindowProperty window1 WindowSize (700.0 700.0))
+
+(NewView view1)
+(ViewProperty view1 Window window1)
+</pre>
+</body>
+</html>
diff --git a/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x1.html b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x1.html
new file mode 100644
index 0000000..42842a4
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x1.html
@@ -0,0 +1,69 @@
+<!--
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+ -->
+
+<html>
+<head>
+ <title>j3d1x1 config file</title>
+</head>
+
+<body text="#000000" bgcolor="#FFFFFF" link="#0000EE" vlink="#551A8B" alink="#FF0000">
+<pre>
+/*
+ ************************************************************************
+ *
+ * Java 3D configuration file for a single fullscreen desktop configuration.
+ *
+ ************************************************************************
+ */
+
+(NewWindow big 0)
+(WindowProperty big WindowSize NoBorderFullScreen)
+
+(NewView view0)
+(ViewProperty view0 Window big)
+</pre>
+</body>
+</html>
diff --git a/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x2-flat.html b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x2-flat.html
new file mode 100644
index 0000000..3bf83f5
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x2-flat.html
@@ -0,0 +1,152 @@
+<!--
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+ -->
+
+<html>
+<head>
+ <title>j3d1x2-flat config file</title>
+</head>
+
+<body text="#000000" bgcolor="#FFFFFF" link="#0000EE" vlink="#551A8B" alink="#FF0000">
+<pre>
+/*
+ ************************************************************************
+ *
+ * Java 3D configuration file for dual-screen (flat) desktop configuration
+ * with no head tracking.
+ *
+ ************************************************************************
+ */
+
+// Create new screen objects and associate them with logical names and numbers.
+// These numbers are used as indices to retrieve the AWT GraphicsDevice from
+// the array that GraphicsEnvironment.getScreenDevices() returns.
+//
+// NOTE: The GraphicsDevice order in the array is specific to the local
+// site and display system.
+//
+(NewScreen left 0)
+(NewScreen right 1)
+
+// Set the screen dimensions.
+//
+(ScreenProperty left PhysicalScreenWidth 0.360)
+(ScreenProperty left PhysicalScreenHeight 0.288)
+
+(ScreenProperty right PhysicalScreenWidth 0.360)
+(ScreenProperty right PhysicalScreenHeight 0.288)
+
+// Specify full screen windows.
+//
+(ScreenProperty left WindowSize NoBorderFullScreen)
+(ScreenProperty right WindowSize NoBorderFullScreen)
+
+// Set the TrackerBaseToImagePlate transforms for these screens. This
+// transforms points in tracker base coordinates to each screen's image plate
+// coordinates, where the origin of the image plate is defined to be the lower
+// left corner of the screen with X increasing to the right, Y increasing to
+// the top, and Z increasing away from the screen.
+//
+// Without head or sensor tracking the tracker base is still needed as a fixed
+// frame of reference for describing the orientation and position of each
+// screen to the others. The coexistence to tracker base transform is set to
+// identity by default, so the tracker base origin and orientation will also
+// set the origin and orientation of coexistence coordinates in the physical
+// world.
+//
+// The tracker base and center of coexistence is set here to the middle of the
+// edge shared by the two screens.
+//
+(ScreenProperty left TrackerBaseToImagePlate
+ (Translate 0.360 0.144 0.0))
+(ScreenProperty right TrackerBaseToImagePlate
+ (Translate 0.000 0.144 0.0))
+
+// Sometimes it is desirable to include the bevels in between the monitors in
+// the TrackerBaseToImagePlate transforms, so that the abutting bevels obscure
+// the view of the virtual world instead of stretching it out between the
+// monitors. For a bevel width of 4.5 cm on each monitor, the above commands
+// would become the following:
+//
+// (ScreenProperty left TrackerBaseToImagePlate
+// (Translate 0.405 0.144 0.0))
+// (ScreenProperty right TrackerBaseToImagePlate
+// (Translate -0.045 0.144 0.0))
+//
+// Conversely, a similar technique may be used to include overlap between the
+// screens. This is useful for projection systems which use edge blending
+// to provide seamless integration between screens.
+
+
+// Create a view using the defined screens.
+//
+(NewView view0)
+(ViewProperty view0 Screen left)
+(ViewProperty view0 Screen right)
+
+// Set the eyepoint relative to coexistence coordinates. Here it is set 45cm
+// toward the user along Z, extending out from the midpoint of the edge shared
+// by the two screens. This will create the appropriate skewed projection
+// frustums for each image plate.
+//
+// If a planar display surface is all that is required, the same effect could
+// be achieved in a virtual screen enviroment such as Xinerama by simply
+// creating a canvas that spans both screens. In some display environments the
+// use of a canvas that spans multiple physical screens may cause significant
+// performance degradation, however.
+//
+// See j3d1x2-rot30 for an example of a non-planar configuration that cannot be
+// achieved through a single canvas spanning both screens.
+//
+(ViewProperty view0 CenterEyeInCoexistence (0.0 0.0 0.45))
+
+(NewViewPlatform vp)
+(ViewPlatformProperty vp AllowPolicyRead true)
+(ViewPlatformProperty vp AllowLocalToVworldRead true)
+
+(ViewProperty view0 ViewPlatform vp)
+</pre>
+</body>
+</html>
diff --git a/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x2-rot30.html b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x2-rot30.html
new file mode 100644
index 0000000..279b9d0
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x2-rot30.html
@@ -0,0 +1,111 @@
+<!--
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+ -->
+
+<html>
+<head>
+ <title>j3d1x2-rot30 config file</title>
+</head>
+
+<body text="#000000" bgcolor="#FFFFFF" link="#0000EE" vlink="#551A8B" alink="#FF0000">
+<pre>
+/*
+ ************************************************************************
+ *
+ * Java 3D configuration file for a dual-screen desktop configuration
+ * with each screen rotated toward the other by 30 degrees about Y from
+ * planar. The inside angle between them is 120 degrees.
+ *
+ ************************************************************************
+ */
+
+// Create new screen objects and associate them with logical names and numbers.
+// These numbers are used as indices to retrieve the AWT GraphicsDevice from
+// the array that GraphicsEnvironment.getScreenDevices() returns.
+//
+// NOTE: The GraphicsDevice order in the array is specific to the local
+// site and display system.
+//
+(NewScreen left 0)
+(NewScreen right 1)
+
+// Set the available image areas for full screens.
+//
+(ScreenProperty left PhysicalScreenWidth 0.360)
+(ScreenProperty left PhysicalScreenHeight 0.288)
+
+(ScreenProperty right PhysicalScreenWidth 0.360)
+(ScreenProperty right PhysicalScreenHeight 0.288)
+
+// Specify full screen windows.
+//
+(ScreenProperty left WindowSize NoBorderFullScreen)
+(ScreenProperty right WindowSize NoBorderFullScreen)
+
+// Set the TrackerBaseToImagePlate transforms for these screens.
+//
+// The tracker base is set here to the middle of the edge shared by the two
+// screens. Each screen is rotated 30 degrees toward the other about the
+// tracker base +Y axis, so that the tracker base +Z is centered between the
+// two screens.
+//
+(ScreenProperty left TrackerBaseToImagePlate
+ (RotateTranslate (Rotate 0.000 -30.000 0.0)
+ (Translate 0.360 0.144 0.0)))
+
+(ScreenProperty right TrackerBaseToImagePlate
+ (RotateTranslate (Rotate 0.000 30.000 0.0)
+ (Translate 0.000 0.144 0.0)))
+
+
+// Create a view using the defined screens.
+//
+(NewView view0)
+(ViewProperty view0 Screen left)
+(ViewProperty view0 Screen right)
+(ViewProperty view0 CenterEyeInCoexistence (0.0 0.0 0.45))
+</pre>
+</body>
+</html>
diff --git a/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x3-cave-vr.html b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x3-cave-vr.html
new file mode 100644
index 0000000..f296dcb
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x3-cave-vr.html
@@ -0,0 +1,243 @@
+<!--
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+ -->
+
+<html>
+<head>
+ <title>j3d1x3-cave-vr config file</title>
+</head>
+
+<body text="#000000" bgcolor="#FFFFFF" link="#0000EE" vlink="#551A8B" alink="#FF0000">
+<pre>
+/*
+ ************************************************************************
+ *
+ * Java 3D configuration file for a cave environment with head tracking and
+ * stereo viewing. This cave consists of 3 projectors with 3 screens to the
+ * left, front, and right of the user, all at 90 degrees to each other.
+ *
+ * The projectors in Sun's VirtualPortal sample site are actually turned
+ * on their sides to get more height. Screen 0 is rotated 90 degrees
+ * counter-clockwise, while screens 1 and 2 are rotated 90 degrees
+ * clockwise.
+ *
+ ************************************************************************
+ */
+
+// Configure the head tracker.
+//
+(NewDevice tracker1 com.sun.j3d.input.LogitechTracker)
+(DeviceProperty tracker1 SerialPort "/dev/ttya") // Unix paths need quoting.
+(DeviceProperty tracker1 TransmitterBaseline 0.4600)
+(DeviceProperty tracker1 TransmitterLeftLeg 0.4400)
+(DeviceProperty tracker1 TransmitterCalibrationDistance 0.4120)
+
+// Configure an InputDevice to use for a 6 degree of freedom wand.
+//
+(NewDevice tracker2 com.sun.j3d.input.LogitechTracker)
+(DeviceProperty tracker2 SerialPort "/dev/ttyb")
+(DeviceProperty tracker2 ReceiverBaseline 0.0700)
+(DeviceProperty tracker2 ReceiverLeftLeg 0.0625)
+(DeviceProperty tracker2 ReceiverHeight 0.0510)
+(DeviceProperty tracker2 ReceiverTopOffset 0.0000)
+
+// Make the tracker2 device a slave of the tracker1 device.
+(DeviceProperty tracker1 Slave (Device tracker2))
+
+// Create logical names for the head tracker and wand sensors. The last
+// argument is the sensor's index in the input device.
+//
+(NewSensor head tracker1 0)
+(NewSensor sensor6d tracker2 0)
+
+// Create new screen objects and associate them with logical names and numbers.
+// These numbers are used as indices to retrieve the AWT GraphicsDevice from
+// the array that GraphicsEnvironment.getScreenDevices() returns.
+//
+// NOTE: The GraphicsDevice order in the array is specific to the local
+// site and display system.
+//
+(NewScreen left 0)
+(NewScreen center 1)
+(NewScreen right 2)
+
+
+// Set the available image areas as well as their positition and orientation
+// relative to the tracker base. From the orientation of a user standing
+// within this VirtualPortal site and facing the center screen, the tracker
+// base is along the vertical midline of the screen, 0.248 meters down from
+// the top edge, and 1.340 meters in front of it. The tracker base is
+// oriented so that its +X axis points to the left, its +Y axis points toward
+// the screen, and its +Z axis points toward the floor.
+//
+(ScreenProperty left PhysicalScreenWidth 2.480)
+(ScreenProperty left PhysicalScreenHeight 1.705)
+(ScreenProperty left WindowSize NoBorderFullScreen)
+(ScreenProperty left TrackerBaseToImagePlate
+ (( 0.0 0.0 -1.0 2.230)
+ ( 0.0 -1.0 0.0 1.340)
+ (-1.0 0.0 0.0 0.885)))
+
+(ScreenProperty center PhysicalScreenWidth 2.485)
+(ScreenProperty center PhysicalScreenHeight 1.745)
+(ScreenProperty center WindowSize NoBorderFullScreen)
+(ScreenProperty center TrackerBaseToImagePlate
+ (( 0.0 0.0 1.0 0.248)
+ (-1.0 0.0 0.0 0.885)
+ ( 0.0 -1.0 0.0 1.340)))
+
+(ScreenProperty right PhysicalScreenWidth 2.480)
+(ScreenProperty right PhysicalScreenHeight 1.775)
+(ScreenProperty right WindowSize NoBorderFullScreen)
+(ScreenProperty right TrackerBaseToImagePlate
+ (( 0.0 0.0 1.0 0.2488)
+ ( 0.0 -1.0 0.0 1.340)
+ ( 1.0 0.0 0.0 0.860)))
+
+// Create a physical environment. This contains the available input devices,
+// audio devices, and sensors, and defines the coexistence coordinate system
+// for mapping between the virtual and physical worlds.
+//
+(NewPhysicalEnvironment VirtualPortal)
+
+// Register the input device defined in this file and the sensor which will
+// drive head tracking.
+//
+(PhysicalEnvironmentProperty VirtualPortal InputDevice tracker1)
+(PhysicalEnvironmentProperty VirtualPortal InputDevice tracker2)
+(PhysicalEnvironmentProperty VirtualPortal HeadTracker head)
+
+// Set the location of the center of coexistence relative to the tracker base.
+// Here it set to the center of the center screen. The default view attach
+// policy of NOMINAL_SCREEN used by ConfiguredUniverse will place the origin of
+// the view platform in coexistence coordinates at the center of coexistence.
+//
+(PhysicalEnvironmentProperty VirtualPortal
+ CoexistenceToTrackerBase
+ ((-1.0 0.0 0.0 0.000)
+ ( 0.0 0.0 -1.0 1.340)
+ ( 0.0 -1.0 0.0 0.994)))
+
+// Define the physical body. The head origin is halfway between the eyes, with
+// X extending to the right, Y up, and positive Z extending into the skull.
+//
+(NewPhysicalBody LabRat)
+(PhysicalBodyProperty LabRat StereoEyeSeparation .07)
+
+// Define the position and orientation of the head relative to the tracker
+// mounted on the head.
+//
+(PhysicalBodyProperty LabRat HeadToHeadTracker
+ ((-1.0 0.0 0.0 0.00)
+ ( 0.0 0.0 -1.0 0.05)
+ ( 0.0 -1.0 0.0 0.11)))
+
+// Create a view platform behavior for the 6DOF sensor.
+//
+(NewViewPlatformBehavior vpb com.sun.j3d.utils.behaviors.vp.WandViewBehavior)
+
+(ViewPlatformBehaviorProperty vpb Sensor6D sensor6d)
+(ViewPlatformBehaviorProperty vpb ButtonAction6D 2 GrabView)
+(ViewPlatformBehaviorProperty vpb ButtonAction6D 1 TranslateForward)
+(ViewPlatformBehaviorProperty vpb ButtonAction6D 0 TranslateBackward)
+
+// Default normal translation speed is 0.1 physical meters per second.
+(ViewPlatformBehaviorProperty vpb TranslationSpeed
+ 1.0 PhysicalMeters PerSecond)
+
+// Default rotation coordinates are Sensor.
+(ViewPlatformBehaviorProperty vpb RotationCoords Head)
+
+// Nominal sensor transform for modified joystick RedBarron
+(SensorProperty sensor6d Hotspot (0.00 0.6 0.00))
+(ViewPlatformBehaviorProperty vpb NominalSensorRotation
+ ((-1.0 0.0 0.0)
+ ( 0.0 0.0 -1.0)
+ ( 0.0 -1.0 0.0)))
+
+// Default 6DOF sensor echo is Gnomon
+(ViewPlatformBehaviorProperty vpb EchoSize 0.015)
+(ViewPlatformBehaviorProperty vpb EchoType Beam)
+
+// Default 6DOF sensor echo color is white
+(ViewPlatformBehaviorProperty vpb EchoColor 1.0 0.7 0.0)
+
+// Default 6DOF sensor transparency is 0.0 (opaque)
+(ViewPlatformBehaviorProperty vpb EchoTransparency 0.4)
+
+// Create a new view platform and set the view platform behavior.
+//
+(NewViewPlatform vp)
+(ViewPlatformProperty vp ViewPlatformBehavior vpb)
+
+// Now define the view.
+//
+(NewView view0)
+(ViewProperty view0 Screen left)
+(ViewProperty view0 Screen center)
+(ViewProperty view0 Screen right)
+(ViewProperty view0 PhysicalBody LabRat)
+(ViewProperty view0 PhysicalEnvironment VirtualPortal)
+(ViewProperty view0 ViewPlatform vp)
+
+// Set the screen scale. This is scale factor from virtual to physical
+// coordinates.
+//
+(ViewProperty view0 ScreenScalePolicy SCALE_SCREEN_SIZE)
+
+// Alternative for explict scaling.
+//
+//(ViewProperty view0 ScreenScalePolicy SCALE_EXPLICIT)
+//(ViewProperty view0 ScreenScale 5.00)
+
+// Enable stereo viewing. Enable head tracking to get the position of the eyes
+// with respect to coexistence. Boolean values may be specified as either
+// true, True, false, or False.
+//
+(ViewProperty view0 StereoEnable true)
+(ViewProperty view0 TrackingEnable True)
+</pre>
+</body>
+</html>
diff --git a/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x3-cave.html b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x3-cave.html
new file mode 100644
index 0000000..cf2b008
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x3-cave.html
@@ -0,0 +1,156 @@
+<!--
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+ -->
+
+<html>
+<head>
+ <title>j3d1x3-cave config file</title>
+</head>
+
+<body text="#000000" bgcolor="#FFFFFF" link="#0000EE" vlink="#551A8B" alink="#FF0000">
+<pre>
+/*
+ ************************************************************************
+ *
+ * Java 3D configuration file for a cave environment. This cave
+ * consists of 3 projectors with 3 screens to the left, front, and right
+ * of the user, all at 90 degrees to each other.
+ *
+ * The projectors in the VirtualPortal sample site are actually turned
+ * on their sides to get more height. Screen 0 is rotated 90 degrees
+ * counter-clockwise, while screens 1 and 2 are rotated 90 degrees
+ * clockwise.
+ *
+ ************************************************************************
+ */
+
+// Create new screen objects and associate them with logical names and numbers.
+// These numbers are used as indices to retrieve the AWT GraphicsDevice from
+// the array that GraphicsEnvironment.getScreenDevices() returns.
+//
+// NOTE: The GraphicsDevice order in the array is specific to the local
+// site and display system.
+//
+(NewScreen left 0)
+(NewScreen center 1)
+(NewScreen right 2)
+
+
+// Set the available image areas as well as their positition and orientation
+// relative to the tracker base. Although this config file doesn't enable
+// head tracking, the tracker base is still needed as a point of reference to
+// describe the position and orientation of the screens relative to the
+// environment.
+//
+// From the orientation of a user standing within this VirtualPortal site and
+// facing the center screen, the tracker base is along the vertical midline of
+// the screen, 0.248 meters down from the top edge, and 1.340 meters in front
+// of it. The tracker base is oriented so that its +X axis points to the left,
+// its +Y axis points toward the screen, and its +Z axis points toward the
+// floor.
+//
+(ScreenProperty left PhysicalScreenWidth 2.480)
+(ScreenProperty left PhysicalScreenHeight 1.705)
+(ScreenProperty left WindowSize NoBorderFullScreen)
+(ScreenProperty left TrackerBaseToImagePlate
+ (( 0.0 0.0 -1.0 2.230)
+ ( 0.0 -1.0 0.0 1.340)
+ (-1.0 0.0 0.0 0.885)))
+
+(ScreenProperty center PhysicalScreenWidth 2.485)
+(ScreenProperty center PhysicalScreenHeight 1.745)
+(ScreenProperty center WindowSize NoBorderFullScreen)
+(ScreenProperty center TrackerBaseToImagePlate
+ (( 0.0 0.0 1.0 0.248)
+ (-1.0 0.0 0.0 0.885)
+ ( 0.0 -1.0 0.0 1.340)))
+
+(ScreenProperty right PhysicalScreenWidth 2.480)
+(ScreenProperty right PhysicalScreenHeight 1.775)
+(ScreenProperty right WindowSize NoBorderFullScreen)
+(ScreenProperty right TrackerBaseToImagePlate
+ (( 0.0 0.0 1.0 0.2488)
+ ( 0.0 -1.0 0.0 1.340)
+ ( 1.0 0.0 0.0 0.860)))
+
+// Set the location of the center of coexistence relative to the tracker base.
+// Here it set to the center of the center screen. This config file will set
+// the location of the user's eyes relative to this point. The default view
+// attach policy of NOMINAL_SCREEN used by ConfiguredUniverse will place the
+// origin of the view platform in coexistence coordinates at the center of
+// coexistence.
+//
+(NewPhysicalEnvironment VirtualPortal)
+(PhysicalEnvironmentProperty VirtualPortal
+ CoexistenceToTrackerBase
+ ((-1.0 0.0 0.0 0.000)
+ ( 0.0 0.0 -1.0 1.340)
+ ( 0.0 -1.0 0.0 0.994)))
+
+// Now define the view.
+//
+(NewView view0)
+(ViewProperty view0 Screen left)
+(ViewProperty view0 Screen center)
+(ViewProperty view0 Screen right)
+(ViewProperty view0 PhysicalEnvironment VirtualPortal)
+
+// Set the user eye position in the display environment. It is set here to
+// 1.340 meters back from the center screen (directly under the tracker), and
+// 1.737 meters from the floor (about 5 ft 8.4 inches).
+//
+(ViewProperty view0 CenterEyeInCoexistence (0.0 0.494 1.340))
+
+// Explict scaling.
+//
+(ViewProperty view0 ScreenScalePolicy SCALE_EXPLICIT)
+(ViewProperty view0 ScreenScale 0.30)
+
+// No stereo viewing for this configuration.
+//
+(ViewProperty view0 StereoEnable False)
+</pre>
+</body>
+</html>
diff --git a/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x3-rot45.html b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x3-rot45.html
new file mode 100644
index 0000000..c3e743e
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d1x3-rot45.html
@@ -0,0 +1,122 @@
+<!--
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+ -->
+
+<html>
+<head>
+ <title>j3d1x3-rot45 config file</title>
+</head>
+
+<body text="#000000" bgcolor="#FFFFFF" link="#0000EE" vlink="#551A8B" alink="#FF0000">
+<pre>
+/*
+ ************************************************************************
+ *
+ * Java 3D configuration file for 3 screens. Left and right screens are
+ * rotated 45 degrees from the center screen.
+ *
+ ************************************************************************
+ */
+
+// Create new screen objects and associate them with logical names and numbers.
+// These numbers are used as indices to retrieve the AWT GraphicsDevice from
+// the array that GraphicsEnvironment.getScreenDevices() returns.
+//
+// NOTE: The GraphicsDevice order in the array is specific to the local
+// site and display system.
+//
+(NewScreen left 0)
+(NewScreen center 1)
+(NewScreen right 2)
+
+// Set the available image areas for full screens.
+//
+(ScreenProperty left PhysicalScreenWidth 0.360)
+(ScreenProperty left PhysicalScreenHeight 0.288)
+
+(ScreenProperty center PhysicalScreenWidth 0.360)
+(ScreenProperty center PhysicalScreenHeight 0.288)
+
+(ScreenProperty right PhysicalScreenWidth 0.360)
+(ScreenProperty right PhysicalScreenHeight 0.288)
+
+// Specify full screen windows.
+//
+(ScreenProperty left WindowSize NoBorderFullScreen)
+(ScreenProperty center WindowSize NoBorderFullScreen)
+(ScreenProperty right WindowSize NoBorderFullScreen)
+
+// Set the TrackerBaseToImagePlate transforms for these screens.
+//
+// The tracker base and center of coexistence are set here to the middle of the
+// center screen. The basis vectors are aligned with the center screen image
+// plate. The left and right screens are rotated 45 degrees toward each other
+// about their shared edges with the center screen.
+//
+(ScreenProperty center TrackerBaseToImagePlate
+ (Translate 0.180000 0.144000 0.000000))
+
+// cos(45) * 0.360 * 0.5 = 0.127279; 0.360 + 0.127279 = 0.487279
+(ScreenProperty left TrackerBaseToImagePlate
+ (RotateTranslate
+ (Rotate 0.000000 -45.000000 0.000000)
+ (Translate 0.487279 0.144000 0.127279)))
+
+// cos(45) * 0.360 * 0.5 = 0.127279
+(ScreenProperty right TrackerBaseToImagePlate
+ (RotateTranslate
+ (Rotate 0.000000 45.000000 0.000000)
+ (Translate -0.127279 0.144000 0.127279)))
+
+// Create a view using the defined screens.
+//
+(NewView view0)
+(ViewProperty view0 Screen left)
+(ViewProperty view0 Screen center)
+(ViewProperty view0 Screen right)
+(ViewProperty view0 CenterEyeInCoexistence (0.0 0.0 0.5))
+</pre>
+</body>
+</html>
diff --git a/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d2x2-flat.html b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d2x2-flat.html
new file mode 100644
index 0000000..7ba903f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/utils/universe/doc-files/j3d2x2-flat.html
@@ -0,0 +1,147 @@
+<!--
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+ -->
+
+<html>
+<head>
+ <title>j3d2x2-flat config file</title>
+</head>
+
+<body text="#000000" bgcolor="#FFFFFF" link="#0000EE" vlink="#551A8B" alink="#FF0000">
+<pre>
+/*
+ ************************************************************************
+ *
+ * Java 3D configuration file for 4 screen projection configuration
+ * arranged in a 2x2 power wall.
+ *
+ ************************************************************************
+ */
+
+// Create new screen objects and associate them with logical names and numbers.
+// These numbers are used as indices to retrieve the AWT GraphicsDevice from
+// the array that GraphicsEnvironment.getScreenDevices() returns.
+//
+// NOTE: The GraphicsDevice order in the array is specific to the local
+// site and display system.
+//
+(NewScreen topleft 0)
+(NewScreen topright 1)
+(NewScreen bottomleft 3)
+(NewScreen bottomright 2)
+
+// Set the available image areas for full screens. This is important when
+// precise scaling between objects in the virtual world and their projections
+// into the physical world is desired through use of explicit ScreenScale view
+// attributes. The defaults are 0.365 meters for width and 0.292 meters for
+// height.
+//
+(ScreenProperty topleft PhysicalScreenWidth 0.912)
+(ScreenProperty topleft PhysicalScreenHeight 0.680)
+
+(ScreenProperty topright PhysicalScreenWidth 0.912)
+(ScreenProperty topright PhysicalScreenHeight 0.680)
+
+(ScreenProperty bottomleft PhysicalScreenWidth 0.912)
+(ScreenProperty bottomleft PhysicalScreenHeight 0.685)
+
+(ScreenProperty bottomright PhysicalScreenWidth 0.912)
+(ScreenProperty bottomright PhysicalScreenHeight 0.685)
+
+
+// Specify full screen windows.
+//
+(ScreenProperty topleft WindowSize NoBorderFullScreen)
+(ScreenProperty topright WindowSize NoBorderFullScreen)
+(ScreenProperty bottomleft WindowSize NoBorderFullScreen)
+(ScreenProperty bottomright WindowSize NoBorderFullScreen)
+
+// Set the TrackerBaseToImagePlate transforms for these screens. This
+// transforms points in tracker base coordinates to each screen's image plate
+// coordinates, where the origin of the image plate is defined to be the lower
+// left corner of the screen with X increasing to the right, Y increasing to
+// the top, and Z increasing away from the screen.
+//
+// Without head or sensor tracking the tracker base is still needed as a point
+// of reference for describing the orientation and position of each screen to
+// the others. The coexistence to tracker base transform is set to identity by
+// default, so the tracker base origin and orientation will also set the origin
+// and orientation of coexistence coordinates in the physical world.
+//
+// The tracker base and center of coexistence are set here to the center of the
+// 2x2 array with its basis vectors aligned to image plate coordinates.
+//
+(ScreenProperty topleft TrackerBaseToImagePlate
+ (Translate 0.912 0.000 0.0))
+(ScreenProperty topright TrackerBaseToImagePlate
+ (Translate 0.000 0.000 0.0))
+(ScreenProperty bottomleft TrackerBaseToImagePlate
+ (Translate 0.912 0.685 0.0))
+(ScreenProperty bottomright TrackerBaseToImagePlate
+ (Translate 0.000 0.685 0.0))
+
+// Create a view using the defined screens.
+//
+(NewView view0)
+(ViewProperty view0 Screen topleft)
+(ViewProperty view0 Screen topright)
+(ViewProperty view0 Screen bottomleft)
+(ViewProperty view0 Screen bottomright)
+
+// Set the screen scale. This is scale factor from virtual to physical
+// coordinates. The default policy of SCALE_SCREEN_SIZE doesn't work well here
+// since in the 2x2 arrangement the individual screens are too small. The
+// explicit scale factor below assumes a normalized range of object coordinates
+// of [-1.0 .. +1.0].
+//
+(ViewProperty view0 ScreenScalePolicy SCALE_EXPLICIT)
+(ViewProperty view0 ScreenScale 0.912)
+
+// Set the user eye position in the display environment.
+//
+(ViewProperty view0 CenterEyeInCoexistence (0.0 0.0 1.0))
+</pre>
+</body>
+</html>